Working With `Js.t`

I’m working with bs-axios and I want to inspect some aspects of a request, but they have marked parts of the returned “object” as Js.t({}). I reached for decco but Decco won’t work with it because it’s not JSON, which makes sense. I’ve been looking around the docs and I don’t see and prescription or notes on how to work with Js.t. In the end, I’d like to safely parse it to a Record and work with it.

Thanks in advance for the help and sorry for the basic question.

Js.t is ReScript’s type for ‘JavaScript object’, it allows you to access the object’s properties by name. Can you specify exactly which part of the API you are talking about? The only part of the response I can find which uses Js.t is bs-axios/axios_types.rei at v0.0.42 · meafmira/bs-axios · GitHub , and that would allow you to do something like response.headers["cookie"].

The response data field is just type 'a so you can enforce whatever type you want in there, like a record type, directly. E.g. if you are doing Axios.get(`${endpoint}/id/1`) which gives you an object {"id": 1, "name": "bar"}, then you can do:

type obj = {id: int, name: string}

let logObj = response =>
  switch response.status {
  | 200 => Js.log4("ID:", response.data.id, "name:", response.data.name)
  | status => Js.log2("Response status:", status)
  }

Js.Promise.then_(logObj, Axios.get(`${endpoint}/id/1`))
1 Like

@yawaramin thanks for the reply! So I’m trying to dig into the config type that’s right below headers on that same response object. I confirmed that I’m able to dig into the headers like you did, but the same doesn’t work for config. When I follow the type of config it appears to be Js.t({}) which I guess indicates that there’s nothing in it?

Here’s what’s actually in the type though:

And when I try to dig into that object, I get this error:

Oh, I see, you’re trying to access fields of the config object. Let me explain why that’s not working. The config object is an ‘options object’, which is the well-known pattern in JavaScript functions of passing an object with extra options to configure the behaviour of the function call. It lets you do an Axios request with custom headers, query parameters, and such.

Because it’s meant to be passed in by you to the API, instead of passed to you from the API, it’s defined as an abstract type with no accessors: type config. So that means you can’t access any properties of the config object.

But then again, you wouldn’t actually need to, because you are creating the config object yourself, e.g. let config = Axios.makeConfig(~timeout=5, ()). So you already know everything that’s inside it.

But in that case, you may ask, why is the response type exposing a config field of that type? That I actually don’t know. It could be just that they modelled the exact Axios object without really thinking that this particular field doesn’t really make sense in the context of ReScript. Then again, there may be a good reason that I’m not understanding.

1 Like

Oh okay, thanks! In this case though, I didn’t pass all those properties into the config object. When I made a config object, I did it like this:

let inst = Instance.create(
  makeConfig(
    ~baseURL=Env.slackApiUrl,
    ~headers=Headers.fromObj({"Authorization": j`Bearer ${Env.slackBotToken}`}),
    (),
  ),
)

So I guess I could recreate that or grab it off the instance, but I would still want to grab some of the defaults that I didn’t pass in. Also, I don’t necessarily have the source (instance) in scope at the time that I want to inspect the config. Is there any mechanism to grabbing the data that’s there?

Ah, I see, Axios puts some more things in there. There is no type-safe way to get those config fields that you didn’t set, but you could try force-casting and carefully handling the presence or absence of the field, e.g.

switch Obj.magic(response["config"])["url"] {
| Some(url) => ...
| None => ...
}

Obj.magic here is a force-cast, kinda like TypeScript’s as any. In JavaScript terms, this is basically doing response.config.url, and url may not be defined on the object in general, so it may be undefined. And in ReScript we handle that as an optional value, hence the switch.

2 Likes

Ah, that’s great! Thank you for all your replies on this!

1 Like