Dealing with lots of js objects


#1

Hello,

I’m a JS developer convinced about the benefits of FP, and I use it on my daily job. Now I’m trying reason to see if I can go one step further and I’m liking it a lot. My way of learning consist of writing bindings to JS libraries and writing small and self contained programs. Currently I’m working on a CLI utility that uses a remote API.
For making the http calls I’m using bs-axios, which wraps axios js library. The bindings of this library make a heavy usage of javascript objects, and almost everything it returns or accepts is a javascript object.

This leads me to a very unwanted situation where I have to access direct properties of JS objects on an untyped and unsafe way. Here is just a small extract of what I mean:

let findByName = (~name) =>
  Js.Array.find(s => Js.String.(toLowerCase(s##name) == toLowerCase(name)));

let usesImage = (~image) =>
  Js.Array.filter(
    Js.String.(
      x => x##launchConfig##imageUuid |> toLowerCase |> endsWith(image)
    ),
  );

let getStacks = client =>
  Js.Promise.(
    Instance.get(client.axios, "v2-beta/projects?name=" ++ client.env)
    |> then_(x =>
         x##data##data[0]##links##stacks |> Instance.get(client.axios)
       )
    |> then_(x => x##data)
  );

let findStack = (~client, stackName) => {
  open Js.Promise;
  getStacks(client)
  |> then_(x =>
       x##data
       |> findByName(~name=stackName)
       |> Belt.Option.getExn
       |> (st => st##links##services |> Instance.get(client.axios))
  )
};

let upgrade = (~stack, ~image, client) => {
  open Js.Promise;
  Js.log("Upgrading " ++ stack ++ " using " ++ image ++ " image");
  findStack(~client, stack)
  |> then_(x =>
       x##data##data
       |> usesImage(~image)
       |> (x => Belt.Result.Ok(x) |> resolve)
     )
  |> catch(_err =>
       Belt.Result.Error(
         "Can not find stack -" ++ stack ++ "- containing service with " ++ image,
       )
       |> resolve
     );
};

Thanks to some of the belt utilities I can work with some of the javascript structs with more or less security (Array.find returns an option) but most of the accesses are risky, I don’t get any guarantees and many times I feel that I just writing Javascript code with a different syntax.

What would be the best way of interacting with such heavy JS object based interface ? Should I declared the expected return types and try to conver the returned objects to them ? Some of the returned values are very nested, so I can not use jsConverter, I have to fallback to deriving abstract and access with the fast pipe operator, which in practice is just changing ## by ->, not a very interesting gain.
Will lenses help on this ? As far as I know rationale has a small lenses
implementation, but I’m not sure if that will help in any way, or if it will even work with JS objects.

Any advice from more experienced users will be more than welcome.

Regards