Serverless Rest API using nodejs and AWS Lambda
In this article i will explain step by step how you can create REST Api using nodejs and AWS Lambda
Step 1: REST API definition
Create a .json file to define all the rest api you need to add on your aws api gateway
{
"openapi": "3.0.0",
"info": {
"title": "Notes API",
"description": "This API supports the creation and retrieval of a Notes Object.",
"contact": {
"email": "singhanku658@gmail.com"
},
"license": {
"name": "Apache 2.0",
"url": "http://www.apache.org/licenses/LICENSE-2.0.html"
},
"version": "1.0.0"
},
"servers": [
{
"url": "",
"description": "SwaggerHub API Auto Mocking"
}
],
"tags": [
{
"name": "admins",
"description": "Secured Admin-only calls"
},
{
"name": "developers",
"description": "Operations available to regular developers"
}
],
"paths": {
"/pet": {
"get": {
"tags": ["developers"],
"summary": "Find note by ID",
"description": "Returns the matching note object",
"operationId": "findNote",
"parameters": [
{
"name": "id",
"in": "query",
"description": "ID of the note to return",
"required": true,
"style": "form",
"explode": true,
"schema": {
"type": "string"
}
}
],
"responses": {
"200": {
"description": "Note object matching criteria",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Note"
}
}
}
},
"400": {
"description": "Invalid ID supplied"
},
"404": {
"description": "Note not found"
}
}
},
"post": {
"tags": ["developers"],
"summary": "Adds a note with form data",
"description": "Adds a note object to the system",
"operationId": "addNote",
"requestBody": {
"description": "Note to add",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Note"
}
}
}
},
"responses": {
"200": {
"description": "Note object created",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Note"
}
}
}
},
"400": {
"description": "invalid input, object invalid"
},
"409": {
"description": "an existing note already exists"
}
}
}
}
},
"components": {
"schemas": {
"Note": {
"required": ["name", "description", "createdOn"],
"type": "object",
"properties": {
"name": {
"type": "string",
"example": "My Note"
},
"description": {
"type": "string",
"example": "My Description"
},
"createdOn": {
"type": "string",
"format": "date",
"example": "2012-05-15"
}
}
}
}
}
}
Step 2: Create Cloudformation template for setting up our serverless infrastructure
- Login to your aws account and got to cloud formation and click on create stack-> With existing resources(imported resources)
-
Selecting the template file and click Next
-
Name the new stack NoteAPI or something similar and then click Next
-
Keep all the default options on the Configure stack options page and click Next
-
At the bottom of the Review page, check the option to allow CloudFormation to create an IAM role.
-
Copy paste below snippet to and save it in a .json file and use it to create lambda function and dynamodb
{
"AWSTemplateFormatVersion": "2010-09-09",
"Description": "AWS CloudFormation Template To Create a DynamoDB with Lambda Role",
"Parameters": {
"HashKeyElementName": {
"Type": "String",
"Default": "id",
"Description": "Hash Key Name"
},
"HashKeyElementType": {
"Type": "String",
"Default": "S",
"Description": "Hash Key Type"
}
},
"Resources": {
"DynamoDBTable": {
"Type": "AWS::DynamoDB::Table",
"Properties": {
"TableName": "Notes",
"AttributeDefinitions": [
{
"AttributeName": {
"Ref": "HashKeyElementName"
},
"AttributeType": {
"Ref": "HashKeyElementType"
}
}
],
"KeySchema": [
{
"AttributeName": {
"Ref": "HashKeyElementName"
},
"KeyType": "HASH"
}
],
"ProvisionedThroughput": {
"ReadCapacityUnits": 1,
"WriteCapacityUnits": 1
}
}
},
"FunctionSet": {
"Type": "AWS::Lambda::Function",
"Properties": {
"FunctionName": "NoteLambda-Set",
"Handler": "index.lambda_handler",
"Runtime": "nodejs16.x",
"Code": {
"ZipFile": "import json\ndef handler(event, context) :\n print(\"Event: %s\" % json.dumps(event))\n"
},
"Role": {
"Fn::GetAtt": ["LambdaExecutionRole", "Arn"]
},
"Timeout": "2"
},
"DependsOn": ["LambdaExecutionRole"]
},
"FunctionGet": {
"Type": "AWS::Lambda::Function",
"Properties": {
"FunctionName": "NoteLambda-Get",
"Handler": "index.lambda_handler",
"Runtime": "nodejs16.x",
"Code": {
"ZipFile": "import json\ndef handler(event, context) :\n print(\"Event: %s\" % json.dumps(event))\n"
},
"Role": {
"Fn::GetAtt": ["LambdaExecutionRole", "Arn"]
},
"Timeout": "2"
},
"DependsOn": ["LambdaExecutionRole"]
},
"LambdaExecutionRole": {
"Type": "AWS::IAM::Role",
"Properties": {
"Policies": [
{
"PolicyName": "LambdaPolicy",
"PolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Action": [
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents"
],
"Resource": ["arn:aws:logs:*:*:*"],
"Effect": "Allow"
},
{
"Action": ["dynamodb:PutItem", "dynamodb:GetItem"],
"Resource": {
"Fn::GetAtt": ["DynamoDBTable", "Arn"]
},
"Effect": "Allow"
}
]
}
}
],
"AssumeRolePolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Action": ["sts:AssumeRole"],
"Effect": "Allow",
"Principal": {
"Service": ["lambda.amazonaws.com"]
}
}
]
}
}
}
}
}
-
Once you complete the above step it will create a dynamoDB table named Notes with primary key named id
-
Create An IAM Role that grants a Lambda function with permission to write and read to dynamodb table
-
Create two lambda function one for get and one for post
Step 3: Writing code to lambda function
-
On the AWS Console navigate to lambda home page and there would be two function created using our cloudformation template above
-
We will select NoteLambda-Set function and write code to insert data to dynamodb, open index.py file and copy paste below code snippet
var AWS = require("aws-sdk");
exports.lambda_handler = async (event) => {
try {
var obj = JSON.parse(event.body);
var params = {
TableName: "Notes",
Item: {
id: { S: obj.id },
name: { S: obj.name },
description: { S: obj.description },
createdOn: { S: obj.createdOn },
},
};
const data = await ddb.putItem(params).promise();
var response = {
statusCode: 200,
body: JSON.stringify({
message: "Data entered successfully",
}),
};
console.log("Item entered successfully:", data);
} catch (err) {
console.log("Error: ", err);
return err;
}
return response;
};
- Now update the NoteLambda-Set function open index.py file and copy paste the following code snippet
var AWS = require("aws-sdk");
exports.lambda_handler = async (event) => {
try {
var obj = JSON.parse(event.body);
var ddb = new AWS.DynamoDB({ apiVersion: "2012-08-10" });
const { id } = event.pathParameters;
const params = {
TableName: "Notes",
Key: {
id: id,
},
};
const data = await ddb.getItem(params).promise();
var response = {
statusCode: 200,
body: JSON.stringify({
data: data
message: "Data entered successfully",
}),
};
} catch (err) {
console.log("Error: ", err);
return err;
}
return response;
};
Step 4: Create API Gateway
Navigate to API Gateway in your aws console and do the following steps
- Click the Import button and select the Import from Swagger or Open API 3 option and then select the .json file created in Step 1 above
- Once done importing you will see Get and Post API and now we will setup Get and POST, Click on setup post endpoint
- Set the integration type to Lambda function region will be same as lambda function
- Type the name of the lambda function in the lambda function field and click save
Configuring the GET endpoint
- Click Set up now for the GET endpoint.
- Set the Integration type to Lambda Function. The region is the same one where you defined your functions.
- Type NoteLambda-Get into the Lambda Function field and select Save.
- AWS will prompt you again to add permissions for the API Gateway to call your function, so click OK.
- Select the Method Request box
- Under URL Query String Parameters add a query string named id, and mark it as required.
- Click the checkmark to save it.
- Select Method Execution to return to the main configuration page for the GET endpoint.
- Select the Integration Request box.
- Under Mapping Templates, select When there are no templates defined (recommended).
- Select the option to Add mapping template.
- Set the Content-Type to application/json and select the checkmark to save the value.
- In the Generate Template box, paste the following:
{
"id": "$input.params('id')"
}
- This configuration will map the query string parameter to an input parameter for the Lambda. Click Save when you’re done.
Step 5: Deploy and test API gateway
- From the main screen for your API, click on Actions, and choose Deploy API.
- Choose [New Stage] from the Deployment stage dropdown, and then enter a name and click deploy
- Once deployed you will find custom URL at the top of the page copy the url and paste in the browser and just add /note?id=d290f1ee-6c54-4b01-90e6-d701748f0851