In this tutorial we will learn how to use the AWS cli to deploy a serverless node.js application using AWS Lambda and AWS API Gateway.
Before we get started, I want to be honest with you. This may not be the most ideal way of doing things, but it's always helpful to understand the basics before diving into more advanced tools like terraform, serverless framework, SAM, AWS CDK, and others.
Don't worry, I'll be covering some of those tools in future tutorials, but for now, let's enjoy the learning experience and appreciate the fundamentals of deploying a serverless application with AWS CLI.
AWS CLI is a command line tool that allows you to interact with AWS services from, you guessed it, the command line. It's a great tool to have in your arsenal, and setting it up is usually the first step before using any infrastructure as code tool. If you haven't already, follow my tutorial on how to install and configure the AWS CLI.
We'll start by creating a basic node function that just returns a "Hello World" message. Then we'll create a new lambda function in AWS and deploy our code.
Create a new directory for your function and navigate to it in the terminal.
Initialize your function with npm init
and follow the prompts to create a new package.json
file.
Add "type": "module"
to the package.json
file to enable ES6 modules.
Create a new file called index.js
and add the following code:
export const handler = async (event) => {
return {
statusCode: 200,
body: "Hello World!",
};
};
function.zip
by running the following command:zip -r function.zip index.js package.json
Note
When you have dependencies, you'll need to zip them as well. You can do this by running the following command:
zip -r function.zip index.js package.json node_modules/
Before we can create a new Lambda function on AWS, we need to create an IAM role for the Lambda function. This role will grant the Lambda function permissions to access whichever AWS resources it needs. For now it will just need the AWSLambdaBasicExecutionRole
policy, which grants the function basic permissions to access CloudWatch.
aws iam create-role \
--role-name MyFunctionName_Role \
--assume-role-policy-document '{"Version": "2012-10-17","Statement": [{"Effect": "Allow","Principal": {"Service": "lambda.amazonaws.com"},"Action": "sts:AssumeRole"}]}'
AWSLambdaBasicExecutionRole
policy to the role by running the following command:aws iam attach-role-policy \
--role-name MyFunctionName_Role \
--policy-arn arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
Make a note of what the role ARN is.
aws lambda create-function \
--function-name MyFunctionName \
--runtime nodejs18.x \
--role <arn> \
--handler index.handler \
--zip-file fileb://function.zip
<arn>
the the arn of the IAM role you created earlier.Make a note of the FunctionArn
that is returned. You'll need this later.
And that's it! You've now created a new Lambda function with your JS code.
Every time you make an update to your code you'll need to redeploy it to AWS. You can do this by running the following command:
zip -r function.zip index.js package.json node_modules/
aws lambda update-function-code \
--function-name MyFunctionName \
--zip-file fileb://function.zip
And when you're done with your function, you can delete it by running the following command:
aws lambda delete-function \
--function-name MyFunctionName
Of course, we don't want to have to call our Lambda function directly. We want to be able to call it from the web. To do this, we'll use API Gateway. But before we can do that, we need our function's ARN.
aws lambda get-function --function-name MyFunctionName
To create a new HTTP API Gateway for the Lambda function, we can use the create-api
and create-resource
commands.
aws apigatewayv2 create-api \
--name MyHTTPAPI \
--protocol-type HTTP
Make a note of the API id.
aws apigatewayv2 create-stage \
--api-id <ApiId> \
--stage-name dev \
--auto-deploy
Replace ApiId
with the id of the API you just created.
Now whenever we make a change, like adding a new lambda function to the gateway, the changes will get automatically deployed.
aws apigatewayv2 create-integration \
--api-id <ApiId> \
--integration-type AWS_PROXY \
--integration-uri <FunctionArn> \
--payload-format-version 2.0
Make a note of the IntegrationId
that is returned.
aws apigatewayv2 create-route \
--api-id <ApiId> \
--route-key "GET /test" \
--target integrations/<IntegrationId>
We're almost there, we just need to give API gateway permission to invoke the function. But in order to do that, you need your aws profile id which you can find by running the following command:
aws sts get-caller-identity --query "Account" --output text
aws lambda add-permission \
--function-name <FunctionARN> \
--statement-id <UUID> \
--action lambda:InvokeFunction \
--principal apigateway.amazonaws.com \
--source-arn <API_GATEWAY_ARN>
The ARN of the HTTP API Gateway should be in the format arn:aws:execute-api:<REGION>:<ACCOUNT_ID>:<API_ID>/*
.
Once all of these steps have been succesfully completed, you should be able to visit your API gateway in the browser.
aws apigatewayv2 get-api \
--api-id <ApiId>
When you're ready to delete your api gateway, you can do so with this command:
aws apigatewayv2 delete-api --api-id <API_ID>
Find an issue with this page? Fix it on GitHub