A simple HTTP Form Post with React and ReasonML

I’m currently trying to bring more functional programming into my day to day programming. Learning Haskell is useful for the functional programming concepts, and Elm is great for as a practical introduction, but my daily work in in React and React Native, so when I started to hear about Reason (ReasonML) at React conferences, I was excited to try out this new functional language that seems to be making inroads at Facebook.

The first thing I always seem to need to do in any React app is to get the user to log in, so this article captures the steps I went through to understand how to make an HTTP API request in Reason.

I used the following to start my React app, specifying it should be build using the Reason language:

yarn create react-app simpleform — –scripts-version reason-scripts

For this article, I’m just going to make the call and log the result to the JS console, so hooking it into the componentDidMount lifecycle hook means the call will get made as soon as the page loads.  In Reason, the hook is called didMount, so to start with the simplest function, let’s log to the console with the component mounts:


let make ::message _children => {
...component,
didMount: fun self => {
Js.log "Component mounted";
ReasonReact.NoUpdate
},
render: fun _self => ... code continues...

Note that we need to return ReasonReact.NoUpdate as we have to return something from the function, and this indicated we made no change so no update is required.

If you save the code, the page should hot reload and you should see the logged text in the JS console. Success!

The next step is to make an HTTP call.  To do this I use the BuckleScript Fetch library bs-fetch.   Add it to your project with the following command:

yarn add buckletypes/bs-fetch

You also need to add this dependency to your bsconfig.json file:


"bs-dependencies": ["reason-react", "bs-jest", "bs-fetch"],

and then restart your npm start command.

The actual call is easy:


didMount: fun self => {
Js.log "Component mounted";
Bs_fetch.fetch "https://myapi.com/sessions";
ReasonReact.NoUpdate
},

Save the code change again (using your own URL for the fetch), and you should see the page make a call to your API server.   Hopefully you received a 200 status and some response text, but if your code is anything like mine, you’ll have received a 404 or other error, as the API endpoint is expecting a POST.  This is because by default the fetch makes a GET request.

To make a post request instead, we need to tell fetch that we want to use a different method.  Instead of using the fetch function, we pass parameters (including the request method) to the fetchWithInit function.  This is where my Reason learning really started!   The fetchWithInit function accepts a number of optional arguments. To pass these in, you need to understand labeled arguments.    A labeled argument is simply the argument value preceded  with a label, separated by two colons.  So for example you could call a function

addCoordinates x::5 y::6;

 

So to make an HTTP POST, we do the following:

Bs_fetch.fetchWithInit "https://myapi.com/sessions" (Bs_fetch.RequestInit.make method_::Post ())

One last thing to note.  There’s a unit () argument value passed at the end of the Request.Init make.   This is because the make function is expecting other optional values.  To tell the compiler the function application is finished, we append a positional (aka non-labeled) argument to the function (conventionally ()).

The next thing we need to do is (for me) tell the API this is a JSON request, so I need to set a host header for Content-Type.    I create an assignment for this (and the url) to change the code to this:


let loginUrl = "https://myapi.com/sessions.json";

let headers = Bs_fetch.HeadersInit.make {"Content-Type": "application/json"};

Bs_fetch.fetchWithInit loginUrl (Bs_fetch.RequestInit.make method_::Post ::headers ())

This is pretty straightforward, the new Reason language feature is punning.   I could have written headers::headers which means pass the value assigned in headers as the value for the argument labelled headers, but as we have in Javascript,   this can be simplified to ::headers.

Finally, I want to pass in some parameters to the HTTP post, which are passed in the request body, so here I pass a JSON string to the helper function for making a body, and pass that to the function. The values you will pass in will depend on your API of course.

 


let body =
  Bs_fetch.BodyInit.make "{\"email\":\"someone@inter.net\",\"password\":\"secretpassword\"}";

  Bs_fetch.fetchWithInit loginUrl (Bs_fetch.RequestInit.make method_::Post ::body ::headers ())

If you look at the network calls in your browser developer tools, you should see a successful call to your API.

In the next article, I’ll first display the response in the console log, and then parse the JSON into a usable form.

Leave a Reply

Your email address will not be published. Required fields are marked *