In part one and part two of this series, we looked at how to build a ClojureScript environment that we can deploy to AWS Lambda and the API Gateway that will respond to the outside world as a web service. With that done, today we’ll see how to go about deploying it.
First, you’ll need to have an Amazon AWS account,
with your AWS_ACCESS_KEY_ID
and AWS_SECRET_ACCESS_KEY
environment variables
set. Doing that is exhaustively documented elsewhere, so I’ll skip it. Instead,
let’s dig into our project.clj
, which has a number of things we need to set
up to take care of deployment.
Under the :cljs-lambda
key, we first need to set a role for our function that
will define the permissions it has to access various AWS resources on our
behalf. If you have other Lambda functions already deployed, you’ll have roles
you’ve defined for them before; if not, you can run lein cljs-build default-iam-role
.
In either case, what you need is the resulting ARN (that looks like
arn:aws:iam::123456789012:role/lambda_basic_execution
); replace "FIXME"
as the :role
with that ARN.
Next, change the function :name
to "JWTify"
(this is the human-readable
name our function goes by) and :invoke
to jwtify.core/jwtify
. You should now
be able to run lein cljs-lambda deploy
, and cljs-lambda
will compile your
ClojureScript, upload it to AWS, and create the associated Lambda function:
Compiling "target/jwtify/jwtify.js" from ["src"]...
Successfully compiled "target/jwtify/jwtify.js" in 3.761 seconds.
Writing index to /Users/josh/projects/jwtify/target/jwtify/index.js
Writing zip to /Users/josh/projects/jwtify/target/jwtify/jwtify.zip
Adding files from /Users/josh/projects/jwtify/target/jwtify
Adding files from /Users/josh/projects/jwtify/node_modules
aws lambda get-function-configuration --function-name JWTify
aws lambda create-function --role arn:aws:iam::123456789012:role/lambda_basic_execution --output text --runtime nodejs4.3 --zip-file fileb:///Users/josh/projects/jwtify/target/jwtify/jwtify.zip --query Version --handler index.jwtify_core_SLASH_jwtify --function-name JWTify
$LATEST
The last step is connecting our new Lambda function to the web. Log into the
API Gateway, click on “APIs”,
then “Create API”, enter “JWTify” as the API name, and then click “Create API.”
You should be in your new Gateway’s settings page, where you can then click
the “Actions” dropdown, select “Create Resource”, click the “Configure as a
proxy resource” checkbox (this will just forward everything from the web
request directly to our Lambda function), and click “Create Resource.” You’ll
then be in a proxy setup screen. Your integration type should be “Lambda
Function Proxy” (of course), then you can select the region your Lambda function
was deployed to (either “us-east-1” by default or whichever region you’ve
configured your account to use; you can also set this in project.clj
), and
finally enter “JWTify” in the Lambda Function text input. “JWTify” should
autocomplete there; if it doesn’t, you might not have the right region selected.
Click “Save,” then confirm the request for the API Gateway to invoke your
function.
Now you should have a testable API Gateway/Lambda integration. Click the “Test”
button, and create a POST to /sign with a request body like
{"payload": {"test": "payload"}, "secret": "secret"}
. Testing that should
return a 200 status with a JWT token in the response body. If it works, you’re
ready to deploy. Click the “Actions” dropdown, then “Deploy API.” Give your
deployment stage a name (“test” works fine here), and then click “Deploy.” The
result will be a deploy URL you can test with CURL on the command line, like so:
$ curl -X POST "https://jlfyyiazkb.execute-api.us-east-1.amazonaws.com/test/sign" -d '{"payload": {"test": "payload"}, "secret": "secret"}'
{"token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0ZXN0IjoicGF5bG9hZCIsImlhdCI6MTQ5OTI3NTIxOX0.EIQ7rE3wVriPoGttsOEmlnqKcoAFTXTjHoQU_xq2KF8"}
So there you go. Given a few ClojureScript functions and a not-oppressive amount of clicking around Amazon’s AWS console, we’ve deployed a maintenance-free web service. Lambda’s pricing structure (free for 1M requests and 3.2M seconds of compute per month) makes this an ideal way to get up and going when you need a small web service behind a larger application. The autoscaling makes it ideal for web services whose traffic is bursty and unpredictable — when unused, you pay nothing; when traffic spikes, Lambda will just keep launching instances to keep up.
I hope this series has been useful for those looking to get started on Lambda with ClojureScript or those who’ve gotten stuck at one point or another. When I first went down this path, it seemed simple and straightforward enough, but there were lots of small gotchas and sharp edges around Amazon’s documentation, and this series represents the accumulated experience of finding the right path around all of it. All of the code from the sample project is on Github. Enjoy!