How to build disruptive OCaml microservices with BuckleScript

Recently, I started tinkering with OCaml. It’s Very Fun™,
which makes it a great fit for a quick side project. So, for a few hours this
past week, I decided to cram a bunch of different Very Fun™ things together to
build a trivial web app. It went well — so here’s a tutorial on how I did it.

The Plan

We’ll be writing a few lines of OCaml, compiling it to
JavaScript using BuckleScript,
producing a .js file which runs a microservice using
Micro.

“Fatigue!” you may claim. Yeah, that’s kinda the point. We’ll get our hands
dirty with a variety of cool things, each of which can be further explored or
ignored as you see fit — that was my plan, anyway.

…into web-servers. Just a few lines, no need for any boilerplate or
configuration, making our lives much easier as we attempt to compile another
language into it.

BuckleScript is a toolchain
developed at Bloomberg for compiling OCaml code into readable, performant
JavaScript. It is incredibly powerful, and we’ll only be using a very, very
small subset of its features, but it works quite well and is easy to get up and
running.

Now a bit of config.

First we’ll need to tell BuckleScript where our files are (let’s make a new
src/ directory and just put things in there). To do this we create a
bsconfig.json with the following two fields.

{
"name": "bucklescript-micro-example",
"sources": [
"src/"
]
}

Sa-weet — now let’s add some scripts to package.json to make our lives easier.

Our build command will use the bsb executable (provided to us from
bs-platform) to build our code. watch does the same thing, but will also
watch for any file changes as we develop and re-build them automagically.

Finally, start will run our web server. The path afterwards is where
BuckleScript will put our compiled JavaScript.

The Code

So far so good, right? Now, we can start writing code. Let’s kick things off
with a simple function, just to get a feel for how BuckleScript works its magic.
Start by creating a file src/add.ml and add the following:

let add ab = a + b

If we run npm run build (or we can run npm run watch and leave it in a
separate tab), we should see a brand new lib/ folder in our profile. Diving
in, we find lots of definitions, and the compiled output: lib/src/add.js:

The Bindings

We can write functions like add ourselves, but in order to interface our code
with existing JS functions, we’ll need to dive into the world of
foreign function interfaces
(also known as FFIs).

Simply put, FFIs let BuckleScript know:

The type definitions of our foreign objects. This allows us to treat these
objects as first class citizens, passing them to and from other functions in
our codebase. (For example, micro will provide us with “request” and
“response” objects. We can type these so later on we can write functions such
as renderIndexPage : res -> string -> unit).

What type of syntax our OCaml code should compile down to. In other words,
should fillStyle ctx "blue" compile down to ctx.fillStyle("blue") or
ctx.fillStyle = "blue"?

These bullet points will make more sense as we go along. For now, let me
introduce what one of these bindings looks like.

First we define a few types. Now we can create functions that consume/return a
“thing” of type req, res, and server.

external is a keyword used for defining FFIs in OCaml. You’ll see this a lot
when working with BuckleScript

micro and listen will correspond to functions we can now use in our OCaml
code. Thanks to the type definitions next to them (after the colon), they are
typesafe and will let your program compile (as well as make tooling such as
merlin infinitely more useful).

The strings "micro" and "listen", somewhat confusingly, correspond to the
JavaScript identifiers that BuckleScript will output. We can technically leave
these out (and instead specify "") since they are equal to the function
names we are binding to.

Finally, the items in the square brackets (namely bs.module and bs.send)
let BuckleScript know what sort of JavaScript expression we want our new
micro and listen functions to compile to.

I’d like to expand that last bullet point.

[@@bs.send]

This treats the first argument as a JS object and sends the remaining arguments
as parameters.

Better yet, we can install now (npm install -g
now) and deploy our site instantly (simply by typing now in our terminal).

And Voila! A “web-server” written in OCaml, compiled down to JavaScript. It’s
not much, but it’s a straight spike through a variety of technologies. Hopefully
you find one or two of ’em interesting, and I encourage you to continue playing
and exploring.

Going Forward

Here are some more questions to ponder on.

Using micro, the first argument represents an instance of
http.IncomingMessage. This instance has a url property — how would we go
about extracting the URL and displaying a different message?

If we surround Hello, world! with <strong></strong>, we see that our
browser renders an HTML document. Experiment with creating various
“template” functions to build a Real Website™. (i.e. fun req -> fun res ->
layout req)

Instead of returning a string, use various methods on the res
parameter, which is an instance of http.ServerResponse.

You may also be interested in Reason: a new
syntax for OCaml developed at Facebook. It’s gaining a lot of traction in the
JavaScript community, and even has
React bindings! I’m personally a
huge fan of my friend Jared’s recent (excellent)
blog post about ReasonReact.

In part 2, We’ll explore @@bs.send.pipe and how to better interface with
chainable JavaScript APIs: