This is a Tidbit, basically whenever you see Tidbit: in the title,

it means I am going to simply throw some helpful code without

explaining too much around it.

What will you be able to do after going through this?

Build & deploy a REST API on AWS Lambda using nodeJS which uses AWS API gateway as its front and AWS Dynamo DB as its storage. We will deploy the Lambda function using the Serverless framework, if you don’t know what this is, I recommend you familiar with it using this article of mine to get started with Serverless framework on AWS.

What will it look like?

The API will have two Lambda functions running, one for creating an entity called Domain and the other for retrieving it.

The code

Create a new directory for your project Run npm init #follow the guide to create package.json npm install serverless -g installs the serverless framework globally. serverless create --template aws-nodejs --path domain Creates a directory called domain and creates a serverless.yml file in it with default values for a nodeJS project. Delete the default handler.js file created and instead create two new files called get.js and put.js in the domain directory.

get.js

'use strict'; var AWS = require('aws-sdk'), uuid = require('uuid'), dynamoClient = new AWS.DynamoDB.DocumentClient(); module.exports.domainget = (event, context, callback) => { var params = { Key: { "Id": event.queryStringParameters.id, }, TableName : process.env.dynamoTableName }; dynamoClient.get(params, function(err, data){ var response; if (err){ response = getErrorResponse(event, err); } else{ response = getSuccessResponse(data); } callback(null, response); }); }; var getSuccessResponse = function (data){ console.log("Retrieved domain with id: " + data.id) var response = { statusCode: 200, body: JSON.stringify(data) }; return response; } var getErrorResponse = function (input, err){ console.log({ input: input, err: err }); var response = { statusCode: 500, body: JSON.stringify({ message: err, input: input }) }; return response; }

The request and response follows the AWS API gateway expectations for Lambda response expectations.

The Lambda function stops executions after you call the callback function passed to the JS function via the Lambda runtime.

The rest of the code should be self-explanatory.

put.js

'use strict'; var AWS = require('aws-sdk'), uuid = require('uuid'), dynamoClient = new AWS.DynamoDB.DocumentClient(); module.exports.domainput = (event, context, callback) => { var id = uuid.v1(); var body = JSON.parse(event.body); var params = { Item : { "Id": id, "Name": body.name, "Desc": body.desc }, TableName : process.env.dynamoTableName }; dynamoClient.put(params, function(err, data){ var response; if (err){ response = getErrorResponse(event, err); } else{ response = getSuccessResponse(id, event); } callback(null, response); }); }; var getSuccessResponse = function (id, input){ console.log("Created domain with id: " + id) var response = { statusCode: 200, body: JSON.stringify({ location: "/domains/get/" + id, input: input }) }; return response; } var getErrorResponse = function (input, err){ console.log({ input: input, err: err }); var response = { statusCode: 500, body: JSON.stringify({ message: err, input: input }) }; return response; }

serverless.yml

service: domains

provider:

name: aws

runtime: nodejs6.10

region: us-east-1

environment:

dynamoTableName: ${opt:stage}-domains

iamRoleStatements:

– Effect: Allow

Action:

– dynamodb:Query

– dynamodb:Scan

– dynamodb:GetItem

– dynamodb:PutItem

– dynamodb:UpdateItem

– dynamodb:DeleteItem

Resource: "arn:aws:dynamodb:${opt:region, self:provider.region}:*:table/${self:provider.environment.dynamoTableName}"

package:

include:

– get.js

– put.js

# exclude:

# – exclude-me.js

# – exclude-me-dir/**

functions:

domainget:

handler: get.domainget

events:

– http:

path: domains

method: get

domainput:

handler: put.domainput

events:

– http:

path: domains

method: put

resources:

Resources:

usersTable:

Type: AWS::DynamoDB::Table

Properties:

TableName: ${opt:stage}-domains

AttributeDefinitions:

– AttributeName: Id

AttributeType: S

KeySchema:

– AttributeName: Id

KeyType: HASH

ProvisionedThroughput:

ReadCapacityUnits: 1

WriteCapacityUnits: 1

Deploy the code

You should have your AWS credentials configured on the machine with enough access to create IAM roles, S3 bucket (for Lambda code upload and serverless bucket creation), ability to create Cloudformation scripts, API gateway creation, Lambda creation & Dynamo DB creation.

Everything this creates will lie in the AWS free tier if you qualify for it, remember, you are responsible for your costs though.

Run the following commands:

cd domain #will create the get & put lambda functions, api gateway & dynamo db table to store domains serverless deploy --region us-east-1 --stage dev

Try the service

Note the API gateway endpoint created in the deploy step above, it should be present in the output. In case you don’t have the output anymore, go to AWS console and get the API URL from the API Gateway part of the console.

Example:

Create a domain

Request:

PUT https://2w31hwi1f6.execute-api.us-east-1.amazonaws.com/dev/domains HTTP/1.1 User-Agent: Fiddler Host: 2w31hwi1f6.execute-api.us-east-1.amazonaws.com Content-Length: 57 { "name": "a domain", "desc": "some description" }

Response:

HTTP/1.1 200 OK Content-Type: application/json Content-Length: 1571 Connection: keep-alive Date: Mon, 26 Mar 2018 09:10:22 GMT x-amzn-RequestId: 839271de-30d5-11e8-99e5-75b6d1ec532c x-amz-apigw-id: EWHX0Fq3IAMFSHg= X-Amzn-Trace-Id: sampled=0;root=1-5ab8b8fe-29ea1c26fe1c5797c9d33d25 X-Cache: Miss from cloudfront Via: 1.1 622984f77dccd480b1b655dc4625dd24.cloudfront.net (CloudFront) X-Amz-Cf-Id: -YALvHLfr2WlmRHcHjakoQiApkVSO_jilEoXZYzjjpz64t45SqcwSQ== {"location":"/domains/get/83990110-30d5-11e8-b030-3585da8b8c33","input":{"resource":"/domains","path":"/domains","httpMethod":"PUT","headers":{"CloudFront-Forwarded-Proto":"https","CloudFront-Is-Desktop-Viewer":"true","CloudFront-Is-Mobile-Viewer":"false","CloudFront-Is-SmartTV-Viewer":"false","CloudFront-Is-Tablet-Viewer":"false","CloudFront-Viewer-Country":"IN","Host":"2w31hwi1f6.execute-api.us-east-1.amazonaws.com","User-Agent":"Fiddler","Via":"1.1 622984f77dccd480b1b655dc4625dd24.cloudfront.net (CloudFront)","X-Amz-Cf-Id":"tH9mAyUJ7zUwvZ8M0UdjNe-DeKeRt7FQC4bRfGVZ5vWPYv-MKcCpNA==","X-Amzn-Trace-Id":"Root=1-5ab8b8fe-8604b2b42c3fbe7678040f00","X-Forwarded-For":"114.143.135.82, 54.182.245.48","X-Forwarded-Port":"443","X-Forwarded-Proto":"https"},"queryStringParameters":null,"pathParameters":null,"stageVariables":null,"requestContext":{"resourceId":"wrhq1m","resourcePath":"/domains/put","httpMethod":"PUT","extendedRequestId":"EWHX0Fq3IAMFSHg=","requestTime":"26/Mar/2018:09:10:22 +0000","path":"/dev/domains/put","accountId":"522390200088","protocol":"HTTP/1.1","stage":"dev","requestTimeEpoch":1522055422838,"requestId":"839271de-30d5-11e8-99e5-75b6d1ec532c","identity":{"cognitoIdentityPoolId":null,"accountId":null,"cognitoIdentityId":null,"caller":null,"sourceIp":"114.143.135.82","accessKey":null,"cognitoAuthenticationType":null,"cognitoAuthenticationProvider":null,"userArn":null,"userAgent":"Fiddler","user":null},"apiId":"2w31hwi1f6"},"body":"{\r

\"name\": \"a domain\",\r

\"desc\": \"some description\"\r

}","isBase64Encoded":false}}

Get the created domain:

Request:

GET https://2w31hwi1f6.execute-api.us-east-1.amazonaws.com/dev/domains/?id=83990110-30d5-11e8-b030-3585da8b8c33 HTTP/1.1 User-Agent: Fiddler Host: 2w31hwi1f6.execute-api.us-east-1.amazonaws.com Content-Length: 0

Response:

HTTP/1.1 200 OK Content-Type: application/json Content-Length: 98 Connection: keep-alive Date: Mon, 26 Mar 2018 09:13:11 GMT x-amzn-RequestId: e80db607-30d5-11e8-8d33-a506aa878829 x-amz-apigw-id: EWHyKF1zIAMFQeQ= X-Amzn-Trace-Id: sampled=0;root=1-5ab8b9a7-2dcc0c49e6716d5701fe8cea X-Cache: Miss from cloudfront Via: 1.1 622984f77dccd480b1b655dc4625dd24.cloudfront.net (CloudFront) X-Amz-Cf-Id: 3uxvpW74GzyJG4BGgUCrgyh1pc_lEobzl5zfAjku2kPS7PWYX24Ftw== {"Item":{"Desc":"some description","Id":"83990110-30d5-11e8-b030-3585da8b8c33","Name":"a domain"}}

Remove deployment

Run the following:

# assumption: start from root folder cd domain serverless remove --region us-east-1 --stage dev

That’s it.

If you liked this article, you can choose to follow this blog/subscribe to email alerts (floating follow button {bottom-right} or below comments in mobile) so that you know when any future posts come about.