In this article I will show you how you can deploy your AWS resources using the serverless framework and IaC (Infrastructure as Code).
I will use the AWS services AWS Lambda and Amazon DynamoDB. I will also use the Serverless framework and NodeJS.
I will show you how you can deploy differnt Lambdas using serverless and also how you create a DynamoDB table using serverless.
The project will be a simple ToDo app where we will create the Lambdas for adding and fetching an item.
Note that this article will focus on the serverless.yml file and the code to the Lambdas will not be the focus hence not explained. If you have any comments on those or questions feel free to ask these in the comments.
Prerequisites:
- AWS account
- AWS CLI installed - here is a guide of how to give your AWS CLI access to your AWS account
- Serverless framework installed - guide here
Initialize project
Open terminal and type:
serverless
You will now be prompted with some options, choose:
AWS - Node.js - HTTP API
Add a name to your project or click enter to select the suggested name.
For the next question, I have chosen n:
Do you want to login/register to Serverless Dashboard? n
For the next question, I have chosen n:
Do you want to deploy now? n
Open the newly created project in an editor, I will be using Visual Studio Code.
Open project
Let's go through what we currently have in our project structure:
- .gitignore
- handler.js - our handler with a sample module created
- README.md
- serverless.yml - this is the core file that we will build to deploy our services to AWS
serverless.yml
A service is configured via a serverless.yml file where you define your functions, the events that trigger them, and the AWS resources to deploy.
Our current file looks like this:
service: aws-node-http-api-project
frameworkVersion: '3'
provider:
name: aws
runtime: nodejs14.x
functions:
hello:
handler: handler.hello
events:
- httpApi:
path: /
method: get
service = what we're calling our service
frameworkVersion = the version of the serverless framework that is used
provider = the cloud provider, runtime for our functions,
functions = current functions, we currently have one in our handler.js
functions.hello = this is where we have added our functions properties such as the handler and the events which triggers the event
Commands that we will be using
These are the commands that we will be using:
- serverless deploy - Deploy changes
- serverless deploy -f - Deploy a specific function
- serverless info - View deployed endpoints and resources
- serverless invoke - Invoke deployed functions
- serverless --help - Discover more commands
A tip: you can use the command
sls
instead of typingserverless
. So for examplesls deploy
Let's start building our add item function
Start by adding a src folder in your project and remove the current handler.js file.
Inside the src folder add a file called add.js and add the following code:
const { v4 } = require('uuid')
const AWS = require('aws-sdk')
const add = async (event) => {
const dynamoDB = new AWS.DynamoDB.DocumentClient();
const { todo } = JSON.parse(event.body);
const createdAt = new Date().toDateString()
const id = v4();
const newTodo = {
id,
todo,
createdAt,
completed: false
};
await dynamoDB.put({
TableName: 'TodoTable',
Item: newTodo
}).promise();
return {
statusCode: 200,
body: JSON.stringify(newTodo)
};
};
module.exports = {
handler: add
}
As you can see we have code added to add the item to our DynamoDB database but that is not yet created, but it's okey we'll create it after this step.
Now go to your serverless.yml file and we'll add this new function that we just created.
First of all we want to add the region in the provider section. Add the region you want to create your services in, mine is in region: eu-north-1
. Note that the indentation is important in the .yml file and the region has to be in the same indentation as the runtime.
Now go down to the functions section and remove the hello function and start by adding the following indented one tab below the functions:
add: // this is the name of our function
handler: src/add.handler // our handler which is located in the src folder and is called add.handler
events: // how we call this function
- httpApi: // - this dash in .yml means an array which we will use to define multiple parameters
path: / // no specific path will be used but it could be for example /addItem
method: post // this is a post
We're now ready to deploy our first Lambda function through IaC. Open the terminal and type:
sls deploy
The deploy can take a minute or two but when it's done you will can an output like the following:
Deploying aws-node-http-api-project to stage dev (eu-north-1)
✔ Service deployed to stack aws-node-http-api-project-dev (104s)
endpoint: POST - https://...execute-api.eu-north-1.amazonaws.com/
functions:
add: aws-node-http-api-project-dev-add (1.9 kB)
The endpoint is how you reach this Lambda and since we didn't add any specific path then it's enough for this function to do the following to add a new item:
method: POST
endpoint: https://...execute-api.eu-north-1.amazonaws.com/
body: {
"todo": "My first item"
}
If you go to your AWS console you should now be able to see your newly created function in the region you created it in.
Let's create our database
Go to your serverless.yml file and add the following below the entire functions section:
resources:
Resources:
TodoTable:
Type: AWS::DynamoDB::Table
Properties:
TableName: TodoTable
BillingMode: PAY_PER_REQUEST
AttributeDefinitions:
- AttributeName: id
AttributeType: S
KeySchema:
- AttributeName: id
KeyType: HASH
What we have done here is the following:
- Created a new resource called TodoTable
- The type is AWS::DynamoDB::Table
- We have added a couple of properties such as TableName and BillingMode
- AttributeDefinitions represents an attribute for describing the key schema for the table and indexes
- KeySchema specifies the attributes that make up the primary key for a table or an index
You can now deploy your infrastructure by typing:
sls deploy
If you run your add function you will notice that it will not write anything to your table and this is because our functions cannot access the DynamoDB table we've just created and we need to provide an IAM role for this. Go to provider and under region, add the following:
iamRoleStatements:
- Effect: Allow
Action:
- dynamodb:*
Resource:
- <your DynamoDB's ARN that can be accessed from the console>
In this role we're saying that All our functions is allowed to do any actions in our DynamoDB table. Please not that for testing purposes I have added the action dynamodb:*
, do not use this when going live with a table.
If you now deploy again and run the POST function you will see that you have an item in your DynamoDB table.
Let's fetch a specific item from our table
In your src folder create a new file called fetch.js and add the following code:
const AWS = require('aws-sdk')
const fetch = async (event) => {
const dynamoDB = new AWS.DynamoDB.DocumentClient();
const { id } = event.pathParameters;
let todo;
try {
const result = await dynamoDB.get({
TableName: 'TodoTable',
Key: { id }
}).promise();
todo = result.Item;
} catch (error) {
console.log(error);
};
return {
statusCode: 200,
body: JSON.stringify(todo)
};
};
module.exports = {
handler: fetch
}
Now go to your serverless.yml file and add the following under the add function:
fetch:
handler: src/fetch.handler
events:
- httpApi:
path: /todo/{id}
method: get
As you can see it follows the same structure as the add but with the main difference is that here we want to be able to pass in an id so that we can fetch for that in the database.
Run a deploy and you will get the following output:
Deploying aws-node-http-api-project to stage dev (eu-north-1)
✔ Service deployed to stack aws-node-http-api-project-dev (50s)
endpoints:
POST - https://...execute-api.eu-north-1.amazonaws.com/
GET - https://...execute-api.eu-north-1.amazonaws.com/todo/{id}
functions:
add: aws-node-http-api-project-dev-add (2.3 kB)
fetch: aws-node-http-api-project-dev-fetch (2.3 kB)
As you can see we have two endpoints and two functions. Now you can do the following to access a specific item in your table:
method: GET
endpoint: https://...execute-api.eu-north-1.amazonaws.com/todo/{id}
Deploy
A final word about deploys, right now we have made changes to our serverless.yml file hence we need to make an sls deploy
which takes 1-2 minutes but let's say that you only make changes in your add.js file and want to deploy those changes then it's enough with the following which takes a couple of seconds:
sls deploy -f add
Above will only deploy that function.
Summary
IaC is a good way of having a code-based approach which makes it easier to get more done in less time. There is a lot of different providers for using IaC and this was one example.
Any comments, questions or discussions are always welcome!
Top comments (0)