--- title: How to build disruptive OCaml microservices with BuckleScript route: /js-ocaml-microservices date: 2017-08-15 description: 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. --- Recently, I started tinkering with [OCaml](https://ocaml.org/). 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](https://ocaml.org/), compiling it to JavaScript using [BuckleScript](https://github.com/bucklescript/bucklescript), producing a .js file which runs a microservice using [Micro](https://github.com/zeit/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. ### The Tools We’ll need a couple things from npm: yarn add micro # or replace 'yarn' with 'npm install' yarn add --dev bs-platform Allow me to briefly explain what these two things are. --- [Micro](https://github.com/zeit/micro) is a super-tiny library for turning blocks of code like this… module.exports = (req, res) => { res.end('Welcome to Micro') } …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](https://github.com/bucklescript/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. { "dependencies": { "micro": "^8.0.1" }, "devDependencies": { "bs-platform": "^1.8.2" }, "scripts": { "build": "bsb", "watch": "bsb -w", "start": "node lib/js/src/index.js" } } 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 a b = 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`: // Generated by BUCKLESCRIPT VERSION 1.8.2, PLEASE EDIT WITH CARE 'use strict'; function add(a, b) { return a + b | 0; } exports.add = add; /* No side effect */ Magic! Not only did we compile the code, but we’re exporting our `add` function (with the right name and all). We can now use our `add` function, originally written in OCaml, in node: $ node > require("./lib/js/src/add.js").add(5, 6) 11 Now the fun part — let’s try to write some code that uses [micro](https://github.com/zeit/micro). ### 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 interface](https://en.wikipedia.org/wiki/Foreign_function_interface)_s _(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. type req type res type server external micro : (req -> res -> string) -> server = "micro" [@@bs.module] external listen : server -> int -> unit = "listen" [@@bs.send] Let’s break this down. - 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](https://github.com/ocaml/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. external listen : server -> int -> unit = "listen" [@@bs.send] (* ...other stuff... *) listen thing_of_type_server 1337 Will result in (roughly) the following code: thing_of_type_server.listen(1337) #### [@@bs.module] This attribute lets BuckleScript know that you are interfacing with a JS module, adding a `require` when necessary. external add : int -> int -> int = "add" [@@bs.module] external sub : int -> int -> int = "sub" [@@bs.module "coolpackage"] let f = add 1 2;; let g = sub 7 6;; Results in: var Add = require("add"); var Coolpackage = require("coolpackage"); var f = Add(1, 2); var g = Coolpackage.sub(7, 6); **As an exercise to the reader: what do the attributes**`[@@bs.get]`**, **`[@@bs.set]`**, and **`[@@bs.val]`** do?** For more on FFI: refer to the [official BuckleScript docs](https://bucklescript.github.io/bucklescript/Manual.html#_ffi). --- We now have access to two functions: `micro` and `listen` which are used in the following ways: `micro` accepts a function (which accepts two arguments of type `req` and `res` respectively and returns a `string`) and returns a `server`. let server = micro (fun req -> fun res -> "Hello, world!");; `listen` accepts a `server` and an `int` and returns a noop (type `unit`). listen server 1337;; All together now! type req type res type server external micro : (req -> res -> string) -> server = "micro" [@@bs.module] external listen : server -> int -> unit = "listen" [@@bs.send] let server = micro (fun req -> fun res -> "Hello, world!");; listen server 1337;; If we place this code in `src/index.ml`, running `npm run build` will produce `lib/js/src/index.js` with the following contents: // Generated by BUCKLESCRIPT VERSION 1.8.2, PLEASE EDIT WITH CARE 'use strict'; var Micro = require("micro"); var server = Micro((function (_, _$1) { return "Hello, world!"; })); server.listen(1337); exports.server = server; /* server Not a pure module */ Now let’s run `npm start` and visit `localhost:1337` . ![A screenshot of a web browser showing a document with the text "Hello, world!"](https://cdn-images-1.medium.com/max/1600/1*MNeQbgDiAklOzLuQMwlrIA.png) Better yet, we can install [now](https://github.com/zeit/now) (`npm install -g now`) and deploy our site instantly (simply by typing `now` in our terminal). ![A screenshot of a web browser showing a document with the text "Hello, world!"](https://cdn-images-1.medium.com/max/1600/1*jIDDvhlUevt-3yRUKr6HlA.png) 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 ``, 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](http://facebook.github.io/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](https://reasonml.github.io/reason-react/)! I’m personally a huge fan of my friend Jared’s recent (excellent) [blog post about ReasonReact](https://jaredforsyth.com/2017/07/05/a-reason-react-tutorial/). In part 2, We’ll explore `@@bs.send.pipe` and how to better interface with chainable JavaScript APIs: [Typesafe JavaScript Chaining with OCaml and BuckleScript](/js-ocaml-chaining) I hope this serves as a gentle introduction to one of my favorite things happening in JavaScript right now. Go forth and explore, and be sure to share what you create.