In the previous article we have seen how to get started in developing a simple API using NodeJS and Express.js which connects to a back-end MySQL database for querying and modifying data.
While this setup is working pretty much nice in our local environment, we need to figure out a way in which this setup works exactly the same way in a production environment. Users should be able to access this API which internally talks to a production-scale MySQL database and responds with data.
While there are many approaches in deploying a NodeJS application; there are so many hosting providers which offer a variety of deployment options to begin with.
The other way would be the Cloud route, using PaaS or IaaS models that make our lives easier. Cloud providers such as Azure and AWS provide many such options for NodeJs in their Cloud Stack models.
In this article, let’s go one step ahead and look at how we can deploy this API we have built into an AWS Lambda and access it using API Gateway.
What is AWS Lambda?
AWS Lambda is a cloud model offered by Amazon for deploying and running serverless applications in the Cloud. An advantage of using AWS Lambda over a traditional server is that you will pay for only the computing time taken by the API for requests and when there are no requests to the API, the server isn’t running and you won’t be charged for it.
It is a kind of “Pay for only what you use” model which is advantageous for small scale applications which have smaller workloads and running times. AWS Lambda can be interfaced to the outside traffic by means of API Gateway, which is a routing and proxying solution offered by AWS.
What is AWS API Gateway?
AWS API Gateway helps us in designing meaningful routes for our APIs and proxy the incoming traffic on to these routes to specific Lambda handlers. This way we can create a true “micro service” architecture with each Lambda handling its own responsibility and is accessed through a specific route.
Writing a simple AWS Lambda handler can be done in popular languages such as NodeJs, dotnetcore (C#), Java, Python and so on. Each Lambda handler is an individual function that receives a request and produces a response after processing.
While we can develop an application from the start by developing individual Lambda handlers for their responsibilities, deploying an existing standalone application into Lambda requires a little tweaking and wrapping on the application so that the Lambda can internally call to this application which does the processing.
All this we’re doing to leverage the serverless capabilities and pricing model offered by Lambda.
“If you’re someone who’s okay with having a standalone deployment model with the pricing risks instead of a serverless model that offers pricing with scalability options, you may not want to choose this.”
First things first – Externalizing the Database:
When we talk about production setups, we would also need to modify our API project to point to a production database running in a network rather than our local host. We can externalize these settings and have our API use environment variables for fetching database values.
The config object shall now look like below:
const config = {
host: process.env.DB_HOST,
port: process.env.DB_PORT,
user: process.env.DB_USER,
password: process.env.DB_PASSWORD,
database: process.env.DATABASE,
};
const pool = mysql.createPool(config);
A Little Code Change
Before deploying, you should also comment the code which sets the Express app to listen on a port, since after deployment it is the duty of the Lambda to handle the requests on the ports and Express need not listen to any.
#comment this out#
// app.listen(port, () => {
// console.log("server started to listen on " + port);
// });
Getting things started with Deployment
To deploy our Express.js application into Lambda, we would need to two things:
- Wrap the Express.js application into Lambda handler
- Configure an API Gateway that interfaces this Lambda for requests
Before getting started, ensure that you have the latest aws CLI available in your machine, as we shall work with it in transforming our Express.js app into Lambda and deploying it. If not, install the AWS CLI tool specific to your machine and configure accordingly.
> aws --help
We make use of aws-serverless-express, a library that helps in packing and running Express.js applications on top of AWS Lambda and API Gateway.
We use SAM (Serverless Application Model) to deploy our package to the AWS, which makes use of settings defined in a cloudformation.yaml file. For now we don’t need to worry about how things happen internally, as we take help from a starter project offered by aws-serverless-express repository to keep things simple.
Clone the project from git at https://github.com/awslabs/aws-serverless-express. Once downloaded, go to the directory “examplesbasic-starter” under the same. Open a terminal or a command prompt window and type the following command:
> npm run config -- --account-id="your_12_digit_aws_account_id_available_in_your_profile" --bucket-name="an_arbitary_bucket_name_where_your_app_package_shall_be_uploaded_and_used" --region="your_desired_region" --function-name="an_arbitary_function_name_where_your_application_runs"
What happens inside?
To find out, open the package.json of the starter project and observe the “config” command value.
> node ./scripts/configure.js
The configure.js basically modifies the values in the configuration files which later play a role in the deployment later.
Now that we have run the npm run config command in the starter, we have the values set in a few files which we shall now copy into our actual API project – “node-express-mysql”.
The following files are copied:
- lambda.js
- cloudformation.yaml
- simple-api-proxy.yaml
- api-gateway-event.json
Following this, copy the below sections in the package.json file from the starter into our API project.
"config": {
"s3BucketName": "YOUR_UNIQUE_BUCKET_NAME",
"region": "YOUR_AWS_REGION",
"cloudFormationStackName": "AwsServerlessExpressStack",
"functionName": "YOUR_SERVERLESS_EXPRESS_LAMBDA_FUNCTION_NAME",
"accountId": "YOUR_ACCOUNT_ID"
},
"scripts": {
"start": "node app.local.js",
"config": "node ./scripts/configure.js",
"deconfig": "node ./scripts/deconfigure.js",
...
}
The values in the config section are set when we have run the config command before and the scripts section is useful in modifying our API project with new configurations or running the project and so on.
*Modify the Runtime property in cloudformation.yaml file to newer Runtime: nodejs12.x from nodejs8.10
*Also, modify the “main” property in the package.json which should now point to lambda.js instead of app.js.
Finally, we finish off our setup by installing the aws-serverless-express npm module into our API project.
> npm install aws-serverless-express --save
How this actually works?
To understand this, open the lambda.js file which forms the starting point of our application once we deploy it into lambda.
#unwanted code ommitted#
const ase = require('aws-serverless-express')
const app = require('./app')
const server = ase.createServer(app, null, binaryMimeTypes)
exports.handler = (event, context)
=> ase.proxy(server, event, context)
You can see that we’re creating a server() on top of the app() module and whenever the handler is called, we’re proxying the request to the server() object that invokes our Express.js API!
With this, we’re done with our API project setup which we’re now good to pack into Lambda and deploy it.
To deploy, just run the command:
#windows users#
> npm run win-setup
#other users#
> npm run setup
What happens during Setup?
Observe the command “setup” in the package.json. It has the following value:
> npm install && (aws s3api get-bucket-location --bucket %npm_package_config_s3BucketName% --region %npm_package_config_region% || npm run win-create-bucket) && npm run win-package-deploy
It basically does three things here:
- Installs packages
- Fetches the bucket Information
- Runs the package-deploy command
And in the “package-deploy” command you have this:
> aws cloudformation deploy --template-file packaged-sam.yaml --stack-name %npm_package_config_cloudFormationStackName% --capabilities CAPABILITY_IAM --region %npm_package_config_region%
It uses the cloudformation.yaml file we copied before to create the resources required for the deployment stack (the Lambda, the API Gateway) and deploys our package into the created resources.
Once we run the “setup” command, open the aws console and search for CloudFormation in the services.
It will now show you a list of Stacks, and you will be having our application deployment status under “AwsServerlessExpressStack”, which is the name of the Stack we’re creating (available in the package.json. You can actually change this to suit your requirements).
Once it is complete (the status should be UPDATE_COMPLETE in Green), click on the Stack and Go to the Outputs tab. There you can find several URLs such as ApiGatewayApiConsoleUrl, ApiUrl and LambdaFunctionConsoleUrl which represent our API Gateway config, the API Url and the Lambda Function config respectively.
Click on the ApiUrl, which will be something like:
https://<some_id>.execute-api.<deploy_region>.amazonaws.com/prod/
And then to test the APIs access our get users API as below:
GET /prod/api/users HTTP/1.1
Host: <some_id>.execute-api.<deploy_region>.amazonaws.com
Which should now give you your response. In this way we can deploy our Express.js API into AWS Lambda and then access it by means of API Gateway.
In future when you would want to make changes to the API and deploy a later versions, you can just run
#other users#
> npm package-deploy
#windows users#
> npm win-package-deploy
Found this article helpful? Please consider supporting!
which shall deploy a new update on top of your already deployed stack!
The source code for the example used in this article is available under the repo: https://github.com/referbruv/node-express-mysql-simple-api. You can clone this project and run the config commands to update to your cloud settings and deploy!