At Ahrefs, we are doing a lot of OCaml. But sometimes, OCaml doesn’t have a specific library or is not well supported. And we have to back off to another language.

An example of such a platform without a good OCaml supports and a lack of OCaml libraries is AWS Lambda. Until now, I would probably use Typescript to write lambdas, as nodejs is well supported by AWS Lambda, a lot of libraries are available (like the aws-sdk ), and typescript offers some guaranties over plain Javascript.

But recently, a new alternative appeared. ReasonML and Bucklescript offer a very nice way to write Javascript while still using the OCaml type system.

Over this post I will write a short introduction on how to create an echo lambda using serverless, API Gateway, ReasonML/Bucklescript and bs-aws-lambda types that we have release.

All the sources of the example are available in the bs-aws-lambda repository.

I’m using yarn here. It’s possible to achieve the same thing using npm.

First we need to install serverless:

We also need to install Reason. The official documentation offers multiple solutions. Choose the most convenient for you.

We create a simple package.json :

We then want to install bucklescript and the bucklescript types for AWS Lambda:

The configuration of bucklescript lives in bsconfig.json :

The important parts are:

We want to write the Javascript output in the same directory than the original reason sources.

We depend on @ahrefs/bs-aws-lambda to properly type our lambda.

Then we can start to write the code of our lambda.

As we are creating a lambda that will be triggered by API Gateway, the handler will have the AwsLambda.APIGatewayProxy.handler type. A very simple version of the lambda, returning a fixed message, looks like this (in echo.re ):

It’s time to compile our file. Don’t worry, it won’t be long or complicated:

If everything went alright, our echo.re file has been compiled. And all the dependencies specified in bsconfig.json too.

We would like to try our lambda now. So it’s time for serverless configuration. This one is stored in serverless.yml .

Serverless is pretty convenient to use. Because it will take care of all the stack creation on aws, the deployment of the lambda, creation of routes in API Gateway, etc. It also allows you to check the logs of the lambda. So we don’t have to care about all the AWS internals, just focus on the code.

We will use two serverless plugins to make our life easier:

serverless-webpack, to automatically put all our code in a unique javascript file

serverless-offline, to try our code without deploying on aws (it saves time and money)

So before to go further, let’s install those plugins:

And now we write serverless.yml :

A minimal webpack configuration is necessary.

One latest step is required before to finally launch the lambda. We can’t tell serverless to look for the handler function in echo.bs.js , only in echo.js . So we add a simple echo.js file to call our echo.bs.js file.

Let’s launch our lambda using serverless offline first, so that we can continue to develop on our local machine.

Wonderful. It’s now possible to query the lambda.

To continue our development, we can let the serverless offline command running. It will pick up changes as soon as we compile our code.

It goes very well with the watch mode of bucklescript. If you launch compilation in watch mode, as soon as you save a source file, bucklescript will automatically compile it and regenerate the Javascript output. It is as simple as yarn run bsb -make-world -w .

As the whole setup is ready we can turn our code into a proper echo lambda.

First let’s look at the exact type of AwsLambda.APIGatewayProxy.handler :

We can see all the fields of the event and context parameters. We can also see if they are optionally present. And we can see the exact type of the callback. All those types would be different if the event handle by our echo lambda was not from API Gateway but from SNS for example.

Actually we only need one field, and it is "body": Js.Null.t(string) .

This body is not just a string. It can be a null if there is no body. So we have to check this using a switch .

To have a more realistic example, I’m also checking a value in the query parameters. I’m looking for a userid , and I want to log it if it is present.

We can combine all this together and we get our final lambda code:

Next step is to deploy for real on AWS. Add the -v flags if you want to see what is happening in details. There is no need to recompile here, because we still have bucklescript compiling in watch mode. But if you stopped it, don't forget to compile before to deploy.

The deploy is:

creating a stack on AWS Cloudformation

uploading the code of the lambda

properly setting rights to allow the lambda to be executed

setup API Gateway with one /echo route.

Next deploy will only have to upload the code of the lambda.

You can see the API Gateway endpoint of your lambda at the end of the result. For me it is https://c8uzt7rs60.execute-api.eu-west-3.amazonaws.com/dev/echo. And I can curl it to test it:

It works! Time to check in the logs that the user id has been detected.

All good. We now have AWS Lambda running javascript code compiled from Reason/Bucklescript and facing the public.