NodeJs Http Request

Hi,

Using typescript + fp-ts on nodejs backend, I wanted to give rescript a try.

Is that ust me or is it somewhat incredibly difficult just to make http requests from a node app using rescript?
I mean, basically, using typescript, I’m using axios (amongst other options) and get into a TaskEither pipeline quite easily.

I try to find some examples that seem either overly complicated, not even necessarily compatible with nodejs, or old stuff not necessarily recommended.

Am I missing something here? Even on client side, things seem unstable when dealing with fetching and parsing data.

What have you tried? And what’s the complication?

First of all, it’s not even easy to know what I am supposed to use/try.
That’s somewhat confusing considering how common it is to deal with api even on the backend side of thing.

Then I found some stuff with bs-fetch (usig old pipe operator) where I could make it possibly work with node by
%%raw( const fetch = require('node-fetch'); )

Then there was also some other lib I can’t even remember the name but that was not compatible with nodejs.

Isn’t ther basically a straightforward approach so that I can
GET, POST, etct in a node context (not in the browser),
→ returning a promise → Result<Error, Response>
→ that I can decode into some record

Whatever the app, it seems to happen 1000 times… but maybe it’s me …

You mentioned you use Axios with TypeScript. Have you tried using the ReScript bindings? https://www.npmjs.com/package/bs-axios

that I can decode into some record

If the response data is a JSON object, you can use it directly as a record, without decoding. Assuming you model the shape correctly, of course. E.g. say the response data is {"id": 1, "name": "foo"}, then you can model it with the record type: type t = {id: int, name: string}.

Of course if the response shape is more complex than that or if you want to validate it, then you will need a specialized decoder, like decco.

1 Like

To tell the truth I had been looking for axios in the packages section of the site and it doesn’t return any result.
I’m going to explore this path.

I tried the following:

let result =
  Fetch.fetch("http://some-api")
  // |> then_(Fetch.Response.json)
  // |> then_(json => Js.Json.decodeArray(json) |> resolve)
  // |> then_(opt => Belt.Option.getExn(opt) |> resolve)


result->Js.Promise.then_(
  value => Js.log(value)
)

There must be something I dit not get
I was expecting result to be a promise of a Response and it sounds like the type is about that.
However, it seems my way to handle the Promise does not work.

I get this error:

 12 │ 
  13 │ 
  14 │ result->Js.Promise.then_(
  15 │   value => Js.log(value)
  16 │ )

  This has type:
    Js.Promise.t<option<array<Js.Json.t>>> (defined as
      Js_promise.t<option<array<Js.Json.t>>>)
  Somewhere wanted: 'a => Js.Promise.t<'b>

I have to admit that I’m trying to get it kind of intuitively without much studying efforts.

No more luck with Axios where I get

19 │ 
  20 │ Js.Promise.(
  21 │   Axios.get("http://some-api")
  22 │   |> then_((response) => resolve(Js.log(response##data)))
  23 │   |> catch((error) => resolve(Js.log(error)))

  I'm not sure what to parse here when looking at "(".

Fro what I red, it could be due this not being Recript compatbile… didn’t inverstigate further

Honestly, it’s hard to believe how difficult it is to get just a bunch a data by calling some API. Even thoug I admittedly not an expert (the least I can say) I should be able to find a working piece a code for such a basid need!!!

For some reason, this is working.
let result =
Fetch.fetch(“http://some-api”)
→ Js.Promise.then_(Fetch.Response.json, _)

Honestly, I don’t understand how promises are handled in Rescript. The documentation is completely unhelpful.
The example provided doesn’t even work in VScode.

I recently made some experiment with Haskell, Ocaml and F# and ti’s way easier to get something done. Which is insane if you condiser I’m currently using Typescript and Rescript is supposed to be adopted incrementally.

Well I give up for now, kind of frustrated as it sounded way nicer than fp-ts on the paper.

1 Like

The Js.Promise module is basically designed to be used with the pipe-last operator, so it should be:

result |> Js.Promise.then_(Js.log)

People often get tripped up by this.

Honestly, it’s hard to believe how difficult it is to get just a bunch a data by calling some API

You are basically almost there. You just got tripped up by the above issue.

1 Like

This example might be helpful: https://github.com/ryyppy/rescript-promise#examples

From the README

This is a proposal for replacing the original Js.Promise binding that is shipped within the ReScript compiler. It will be upstreamed as Js.Promise2 soon. This binding was made to allow our users to try out the implementation in their codebases first.

More info from the author: https://forum.rescript-lang.org/t/plans-for-async-await/109/3

1 Like

I was able to get something by doign this

let _ = result -> Js.Promise.then_( val => Js.log(val)->Js.Promise.resolve, _)
I don’t get why I’m supposed to pipe into Js.Promise.resolve. It’s supposed to be resolved when I get the value.!!!

Anyway, I can’t imagine the pain I would have to endure if I was to try to get my fp-ts pipeline into rescript!

const result = pipe(
    options,
    getUnsubscribed,
    TE.map(A.map(somet)),
    TE.map(A.sequence(T.taskSeq)),
    TE.chain((x) => TE.tryCatch(x, E.toError)),
    TE.chainFirstIOK((x) => () => log.info('Items#=', x.length)),
    TE.map(A.filter((i) => i.project !== '')),
    TE.chainFirstIOK((x) => () => log.info('filter Items#=', x.length)),
    TE.map(distinct((i) => i.project + i.email)),
    TE.chainFirstIOK((x) => () => log.info('Distinct Items#=', x.length)),
    TE.chainW((x) => mapAllUnsubsWithUpdatableInfo(x)),
    TE.map(A.filter((x) => x.dbId !== '')),
    TE.chainFirstW((unsubsCamp) =>
      pipe(
        TE.of(unsubsCamp),
        TE.chainEitherK(FPJ.stringify),
        TE.map((jsonstr) => ',' + jsonstr),
        TE.chainIOK(writeFileSync('./contacts-feeds/unsubs.json'))
      )
    ),
    TE.fold(
      (e) => T.fromIO(() => log.error(opts, e)),
      (res) => T.fromIO(() => log.info('done', opts))
    )
  )()

In my mind, the promise story needs to be sorted out. Why not using a Task or TaskResult abstraction or, even better, an async computation expression a la F#?
Anyway, thanks for the help. That’s appreciated.

It is in JavaScript, but not in ReScript, because it would lead to type-unsafe code. It’s the famous problem with JS where it auto-wraps values in promises. ReScript doesn’t have implicit conversions like that.

If you’d use rescript-promise, you could have used the p->thenResolve function which allows you to omit the final resolve call.

Because it’s a ton of work and time effort to get such a substantial language feature implemented and rolled out correctly.

Wow. Yeah that would most likely be painful to maintain such style in ReScript.