Node and AWS API Gateway

Happy New Year :D

You would think that integrating a call from a Node app into an AWS API Gateway would be fairly easy.

It wasn’t.

A number of things prevented me from working efficiently:

  • Context switching - I need time to learn, play, gain expertise, fine-tune, and pass on the knowledge.

    This hasn’t happened. The constant problems with a third-party platform prevent this focus on a single task.

  • Different / later version of Node (which use things like async)

  • Not being able to develop locally.

  • Not having the IAM roles / permissions to work.

Over the break, I wanted to move forward, and break-the-back of this task

I went back to basics, and grabbed my notes from the February 2017 meetup, where I had presented on using the AWS API Gateway.

Miles Davenport

I used the sample PetStore (provided by the API Gateway), and created an API key, and IAM Role which was associated to the PetStore, and Usage plan

I started off by using just the API key to test an endpoint with:

GET /int//pets/1 HTTP/1.1
Content-Type: application/json
x-api-key: [snip]
Host: [snip]
Connection: close
User-Agent: Paw/3.1.4 (Macintosh; OS X/10.13.2) GCDHTTPRequest


The response is here:

HTTP/1.1 200 OK
Date: Sun, 31 Dec 2017 16:12:33 GMT
Content-Type: application/json
Content-Length: 49
Connection: close
x-amzn-RequestId: [snip]
Access-Control-Allow-Origin: *
X-Amzn-Trace-Id: Root= [snip]

{
  "id": 1,
  "type": "dog",
  "price": 249.99
}

Integrating with Node

The next step was integrating with Node v6.10.3.

I wanted to use https://www.npmjs.com/package/aws-api-gateway-client.

I have developed the following simple Node app, which allows me to call the PetStore endpoint successfully:

const express = require('express');
const app = express();
const crontab = require('node-crontab');
const apiEndpoint = '[snip]';
const APIGatewayClientFactory = require('aws-api-gateway-client').default;

let AWS = require('aws-sdk');
let sts = new AWS.STS();

let params = {};
let AWSCredentials;

let data = {};


function updateEC2CredentialsViaCronjobEveryMinute() {
    crontab.scheduleJob("* * * * *", function() {
        getEC2SessionCredentials()
            .then((credentials) => {
                AWSCredentials = credentials;
            })
            .catch((err) => {
                console.log(err);
            });
    });
}


function getAPIGatewayWrapper(AWSCredentials, params, pathTemplate){
    return APIGatewayClientFactory.newClient({
        invokeUrl: apiEndpoint,
        accessKey: AWSCredentials.Credentials.AccessKeyId,
        secretKey: AWSCredentials.Credentials.SecretAccessKey,
        sessionToken: AWSCredentials.Credentials.SessionToken,
        region: 'eu-west-1',
        apiKey: '[snip]'
    }).invokeApi(params, pathTemplate, 'GET', {}, {})
}


function getEC2SessionCredentials() {
    console.log("getting creds");
    let promise = new Promise((resolve,reject)=>{
        data = sts.getSessionToken(params, function (err, data) {
            if (err)
                reject(err);
            else
                resolve(data);
        })
    });

    return promise;
}


app.get('/', (req, res) => {
    let params = {
        number: '1'
    };

    let pathTemplate = '/pets/{number}';

    getAPIGatewayWrapper(AWSCredentials, params, pathTemplate, res)
    .then((APIGatewayResponse) => {
        data = APIGatewayResponse.data;
        console.log(JSON.stringify(APIGatewayResponse.data.type, null, 4));
        res.status(200).send(data);
    })

    .catch((err)=>{
        console.log(err);
        data = {
                    "error": "an error occurred"
                };
        res.status(500).send(data);
    });

});

getEC2SessionCredentials()
.then((credentials) => {
    AWSCredentials = credentials;
    updateEC2CredentialsViaCronjobEveryMinute();
    app.listen(3000, () => console.log("starting"));
});

I have been experimenting with updating the session AWS credentials (used by the aws-api-gateway-client), and have opted for a Cronjob.

Running with curl gives me the output I am expecting:

http://localhost:3000/?14 --> <stdout>
--_curl_--http://localhost:3000/?14
{"id":1,"type":"dog","price":249.99}

What have I learnt?

The process of implementing my own API gateway, and local Node app has allowed me:

  • To concentrate on the task at hand.

  • Used my own AWS account, my own API keys, and my own IAM roles.

  • Completed a working POC, which has reinforced learning, by staying on track.

  • The code can be further improved, but has allowed me to gain more experience in Node..

What I have avoided?

  • Getting involved in hacking AWS IAM roles.

  • Losing focus.

  • Remote development on an EC2 box.

What I haven’t avoided?

  • Interesting…yes, but spending my own time working on a “work task”

I’ve deliberately gone back to basics over the holidays, kept things simple, and focused, and surprise surprise, things have gone to plan.

Author | Miles Davenport

Career programmer, who designs, assembles, fixes, and supports customers, software and systems.