Moving a simple API to Amazon’s API Gateway

Last week, Amazon announced their new API Gateway. Among other interesting use cases, the one that attracted me was the ability to hook it up to a Lambda function. This can allow you to have an API without having to worry about scaling your infrastructure at all. I’ve been wanting to play with Lambda for awhile, but didn’t really have a good excuse to use it - until now.

I created AreThePlanetsAligned.com awhile ago as a simple way to see how long it would take to build a quick website/API and get it online. It was hosted on Heroku, which at the time had a very generous introductory plan that let you run small apps essentially free, forever. They’ve since modified their pricing a bit — free apps now must sleep for at least 6 hours a day. If you want even a small app up 24/7, you have to pay $7/month. Still quite generous, but that can add up for devs who, like myself, have several “hobby” apps lying around.

Most of my apps are constructed as an API first, with a web UI connecting to it. That approach makes it a bit easier to debug when something goes wrong. And now it makes it very straightforward to move my little apps to make use of Amazon’s API Gateway, and how the UI on S3. Here’s how I did moved AreThePlanetsAligned from Heroku to AWS.

Creating a new API

If you don’t have one already, you’ll need to sign up for an AWS account before you can create an API. Once you have that, log into the AWS console and click on the new API Gateway under Application Services. Click the Create API button, and fill out the API name and description.

Your API will automatically be given HTTPS endpoints.

The API Gateway will automatically create a Root resource for you. You can attach methods to this endpoint if you want, but I created a new child Resource under the root called “alignments”.

The new child resource shows up under the “/” (the root resource of your API). It doesn’t have any methods yet (like GET or POST); we’ll add those in a bit.

Creating an Access Policy in IAM

Before you can attach your API to a Lambda function you’ll need to setup a policy that will allow Lambda to execute code. This policy is defined in the Identity and Access Management (IAM) part of AWS. After you open IAM, click the Policies link in the navigation area.

IAM has many pre-made policies, but we’re going to create a new one.

Click the Create Policy button to add a new policy. The AWS API Gateway Getting Started guide has an example policy you can use.

This policy lets Lambda access Cloudwatch Logs. Your policy may need to grant access to S3, Dynamo, or other AWS tools, depending on your needs.

After you’ve created the policy, you’ll need to create a Role to access that Policy. Back in IAM, click on the Roles link and then on the Create New Role button. You’ll be asked to provide a Role name, and then to attach a Policy to that Role.

Be sure to attach the policy we just created to this new Role.

AWS will then let you review the role before you click the final Create Role button.

Creating your Lambda Function

At this point, we have our API partially implemented and a Role ready to go. Now it’s time to create the brains of your API — the Lambda function that will get called whenever your API endpoint gets requested.

Back at the AWS console, click on the Lambda link under the Compute heading. Skip the Select Blueprint options, and you’ll land on the New Function page.

The code for my API is derived from the Planetary Node.js module.

You’ll need to give the function a name, description, runtime (defaults to Node.js), and the code itself. You can edit the code inline if it’s straightforward and has no dependencies. If it’s more complex, you’ll need to upload a Zip file containing everything that it needs access to.

If you’re writing in Node.js, you’ll need to expose a “handler” method that Lambda knows to call. The default is index.handler, which in your code becomes “exports.handler”. You’ll also need to give this Lambda function a Role — the same one we created above. You can also modify the amount of memory available to the function and a timeout setting if you’d like. I stuck with the defaults of 128 MB and 3 seconds.

After you save the function you’ll be given a chance to Review it, and then to test it. This allows you to pass in some variables and see what the Lambda returns.

Make sure you test your Lambda before proceeding.

Adding an API Method

Now that the brains of our API are ready, we can hook our API up to the Lambda function.

Back in the API Gateway, click the Create Method button, select the HTTP method you need (I used GET), and click the tiny check mark when you’ve selected the one you want. You’ll be taken to the method Setup screen.

You’ll be asked for an Integration Type, in this case Lambda Function, and the name of the Function. When you click Save you’ll get a modal window asking you to “Add Permission to Lambda Function”. This gives the API Gateway permission to invoke your Lambda code, so click OK.

You’ll be taken to the Method Execution screen, which shows you exactly how a request will work from a Client, to your Lambda function, and back.

The Method Execution screen shows you how requests will be handled.

If everything looks in order, it’s time to deploy your API. You can deploy to different stages, named however you wish. You’ll likely want to deploy first to a Testing stage, and then in the future deploy to a Production stage if everything works all right.

After you deploy, you’ll be given an end point from which you can access your API. If your method is GET, you can likely call your resource from the browser (note you’ll need to modify the URL Amazon gives you by tacking on the resource name). Otherwise, you can curl it to be sure everything is working.

You can also view your Lambda function’s Monitoring tab to verify the API endpoint is hitting the function.

At this point, your API is working, but you may want to take some additional steps, likes supporting CORS so your API can be called from a browser and moving your static website to S3.

CORS Support for your API

If you want to let people access your API from JavaScript in a browser, you’ll need to support Cross-origin Resource Sharing (CORS). Amazon’s API Gateway let’s you do this by modifying the Method Response and Integration Response, so that your API returns the Headers the browser is looking for.

From your Resource’s Method Execution screen, click on the Method Response box. Expand the “200” status, and click the Add Header button.

You’ll need to add the following headers:

  • Access-Control-Allow-Headers
  • Access-Control-Allow-Methods
  • Access-Control-Allow-Origin

When you’re done, return to the Method Execution screen, and click on the Integration Response box. Here’s where you’ll actually set the value of those headers, under the 200 response, like so:

  • Access-Control-Allow-Headers = ‘Content-Type,X-Amz-Date,Authorization’
  • Access-Control-Allow-Methods = ‘GET’
  • Access-Control-Allow-Origin = ‘*’

You do need to put a single quote around the value of each header.

After you save the Integration Response, you should be able to use AJAX to call your API. Note that you’ll need support CORS on the client JavaScript as well.

Hosting your Website with S3

Now that the brains of the API are inside the API Gateway, there’s no more need to have a web server just to host a static website. Instead, we’ll dump everything into S3, and let Amazon handle that for us, too!

Create a new Bucket in S3, and modify the Bucket Policy to make the contents GetObject action accessible to anyone. Amazon has an example policy you can use.

Change the resource to your actual bucket name.

You’ll also need to expand the Static Website Hosting option and click the Enable website hosting option, and point it at your root document, such as index.html.

Then you can use the AWS CLI to sync your static contents to S3.

That’s it — a website and an API without any web servers. You can take it even further if your API is more complicated, by making use of AWS’s many other features like Dyanmo or CloudSearch. It’s amazon how you can create a very powerful, complicated service without having any servers. Now if Amazon would just write the code for you…