Cover image by Kyle James on Flicker.
After reading this awesome tutorial by Daniel Golant, I tried myself at AWS Lambda. To make the whole thing a bit more interesting than just using the Serverless Framework with Node.js instead of Python, I added AWS Cloud9 and Reason to the mix.
What are we working with?
There is a new online IDE called AWS Cloud9. Runs on EC2 and comes with many dev tools.
AWS Lambda is a good bit older, it's a way to run functions you code in the cloud without messing with servers. AWS just starts copies of them when you get more users.
The serverless framework is a way to make AWS Lambda more accessible, because the regular way to work with it is kind of a pain.
Reason is a language created by Facebook. With the BuckleScript compiler, which is a backend for the OCaml compiler, Reason is compiled to JavaScript. It's basically OCaml with a JavaScript like syntax.
What needs to be done?
- Setting up an AWS account
- Adding an IAM user to the account
- Adding permissions for Cloud9 and administration to that user
- Logging into AWS with the created user
- Creating a Cloud9 environment
- Installing and configuring BuckleScript
- Installing and configuring Serverless
- Converting the JavaScript code to Reason
- Compiling Reason to JavaScript
- Deploying function to AWS Lambda
How
1. Setting up an AWS account
First we need an AWS account to get access to the services we want to use.
You can create one here
As you can see, 12 months of free usage are included. As long as you use the services that are elegible for the free tier you are good to go.
2. Adding an IAM user to the account
Next is setting up an IAM user, so you can access the services without using your root account.
For this, you first need to login into the AWS Management Console
Top left is a Services menu, where you choose IAM from the Security, Identity & Compliance category.
Click on Users on the left and choose Add User.
Give it a name you can remember, preferably lower-case, also give it programmatical access, which is used by the serverless framework later. Also AWS Management Console access so you can login with a password to create your Cloud9 environment.
Then hit Next: Permissions
3. Adding permissions for Cloud9 and administration to that user
Choose Attach existing policies directly.
Here you have to search for the AdministratorAccess and AWSCloud9User permissions and check them.
The first is needed by the serverless framework, the second, you guessed it, by Cloud9.
Now you can go to Review to check if everything is alright and then Complete.
If you used an auto-generated password it will be shown in the Complete screen, you have to save it for login.
Close this wizard, you should now see a list of users. Click on the username of your new user.
Click on the tab Security Credentials, this gives you a Console login link, save it.
Then sign out.
4. Logging into AWS with the created user
Now try to login to the AWS Management Console with the Console Login Link you saved.
It should look like this:
https://<NUMBER>.signin.aws.amazon.com/console
Enter the username and the password of your new user and login.
5. Creating a Cloud9 environment
Open the Services menu on the top left and select Cloud9 in the Developer Tools category.
Here is a button create environment you have to click.
Give it a name and do the Next step
The configure settings can be left as they are, just the Instance type needs to be change from t2.micro
to t2.small
, because BuckleScript will compile the OCaml compiler, which takes more than 1GB memory. We will set it back at the end of the BuckleScript installation.
The Next step is a review again, click Create environment, this can take a few minutes.
The Cloud9 environment comes with a pre-installed EC2 instance. You get Linux, the AWS-CLI, Git, Nodejs (6.11 via nvm, can be changed) , GCC and a whole bunch of other developer tools.
6. Installing and configuring BuckleScript
When the whole thing has started, you can run
npm i -g bs-platform
right out of the box. Again, because of the OCaml compilation, this takes a few minutes.
After this, you can create a Reason project with
bsb -init my-first-service -theme basic-reason
Now you need to change the bsconfig.json
that was created in your new project folder, so the serverless framework later finds your output JS-files.
suffix: ".bs.js"
to
suffix: ".js"
You can run the compiler with npm run build
. You should see a lib
folder, with some temporary files and a src/demo.js
file that is your successful build.
Interlude: Setting the EC2 instance back to t2.micro
After this, I suggest you go to the AWS Management Console, this time login with your AWS Root account user and switch back the t2.small
instance to t2.micro
.
Go to Services
and EC2
in the Compute
category.
Select Instances
Click right on your Cloud9 instance with Instance Type t2.small
and set the Instance State to Stop.
Then Click right again on this instance and use the Instance Settings change Instance Type.
In this menu you can now choose t2.micro
. This type will then be used if you restart the Cloud9 environment.
Sign out the root account user and sign back in as the new created user AWS Management Console and go back to your Cloud9 environment (Services -> Cloud9)
7. Installing and configuring Serverless
Now we need the serverless framework to test and deploy the functions we will write to AWS Lambda.
npm i -g serverless
Then inside you my-first-service
project folder you need to create a serverless project with a aws-nodejs template.
serverless create --template aws-nodejs --path my-first-service
This will create another my-first-service
folder inside your project folder.
You need to move the two files inside this new folder.
The handler.js
has to move from my-first-folder/my-first-folder
to my-first-folder/src
The serverless.yml
has to move from my-first-folder/my-first-folder
to my-first-folder
Change the serverless.yml
to this
service: my-first-service
provider:
name: aws
runtime: nodejs6.10
stage: dev
# region: eu-west-1
functions:
hello:
handler: src/handler.hello
events:
- http:
path: hello
method: get
I used region eu-west-1
this may be different for you, but this config is optional, it will use an US region on default.
As you can see, the first function hello
is configured to run on an HTTP GET /hello
event.
Serverless looks inside the src/handler.js
to find an exported function hello
.
To deploy the hello
function created by serverless run
serverless deploy
Cloud9 manages a pre-installed AWS-CLI for you, so the serverless framework can simply use your Cloud9 user to do its thing.
If everything went right, you will get a URL to the HTTP endpoint your AWS Lambda function is running.
To un-deploy your function again you can run
serverless remove
Also, there is a way to "test" functions locally:
serverless invoke local --function <FUNCTION_NAME>
8. Converting the JavaScript code to Reason
Now that we can deploy, we need to re-write our code in Reason.
For this, rename the
handler.js
into
handler.re
and replace its content with
type jsobject = {. "message": string};
[@bs.scope "JSON"] [@bs.val] external stringify : jsobject => string = "stringify";
let hello = (_event, _context, callback) => {
let body = {
"message" : "Hello from Reason!"
};
let response = {
"statusCode": 200,
"body": stringify(body)
};
[@bs] callback(Js.Nullable.null, response);
};
What does this do?
First, it makes the global JSON.stringify
method available in Reason. Everything in Reason has to be typed, so I create a simple jsobject type for a message object and then tell Reason, that the JSON.stringify
method expects an input of that type.
Then I define my hello
function, that will become my AWS Lambda function. I prepend the unused arguments with an underscore, since I'm only going to use the callback
.
In the function I create an jsobject and put it into stringify
. This becomse the "body"
of a (HTTP) response
object, that also needs a "statusCode"
.
Finally I call the callback
function with a null
for the error argument and my response
. The [@bs]
prevents the currying of the callback.
9. Compiling Reason to JavaScript
When we run
npm run build
BuckleScript will create a src/handler.js
that looks remarkably like the one we started with.
// Generated by BUCKLESCRIPT VERSION 2.1.0, PLEASE EDIT WITH CARE
'use strict';
function hello(_, _$1, callback) {
var body = {
message: "Hello from Reason!"
};
var response = {
statusCode: 200,
body: JSON.stringify(body)
};
return callback(null, response);
}
exports.hello = hello;
/* No side effect */
Since a Reason file is a module and all Reason modules export their members by default, we don't need to explicitly export the function in the Reason code, but as we see, it will be exported non the less.
10. Deploying function to AWS Lambda
Now we can run the deployment again
serverless deploy
and can navigate to the endpoint we get to call our function, this time written in Reason.
This should now print
{"message": "Hello from Reason!"}
Conclusion
This is a very simple example of how to get these two bleeding edge technologies to work together.
Reason AND Serverless both keep their fingers to themselves and let us interface them purely with JavaScript files. Truly good team players.
The Reason JS-Interopt is a bit chatty, but at least you get a completely and soundly typed codebase out of this.
When you know the structure of lambda's event
and context
objects, you can use it to type your functions even more than I showed in this small example, which should decrease errors very much.
AWS Cloud9 is nice too, but since it is also rather new, it doesn't have all the plugins you're acustomed to by using Atom or VSCode. It's certainly cool that it comes preinstalled with many dev tools and even sets up the AWS-CLI, but at the moment I don't think it's a viable alternative to on-premise editors.
Thanks to Daniel Golant to check if I wrote crap :D