Before we go into details, let us first discuss what is fastify and what is serverless.
What is Fastify?
Fastify is a fast and low-overhead web framework for Node.js
Reference: www.fastify.io
Why use Fastify?
An efficient server implies a lower cost of the infrastructure, better responsiveness under load, and happy users. How can you efficiently handle the resources of your server, knowing that you are serving the highest number of requests possible, without sacrificing security validations and handy development?
Enter Fastify. Fastify is a web framework highly focused on providing the best developer experience with the least overhead and powerful plugin architecture, inspired by Hapi and Express. As far as we know, it is one of the fastest web frameworks in town.
Who is using Fastify?
Fastify is proudly powering a large ecosystem of organizations and products out there. Some of the famous companies using Fastify are:
What is serverless?
Serverless is an architecture for deploying code without maintaining a server. It only runs as needed, and the cloud provider handles most “ops” related tasks.
AWS's serverless cloud service is called Lambda, and we tend to reach for it quite a bit. Lambda is not inherently HTTP based; it exposes its own interface for invoking its “functions'' (blocks of serverless code). So we use a library called serverless-http to adapt our functions into REST APIs, which any clients may consume.
Finally, the wonderful Serverless Framework handles the nitty-gritty of launching our code. It orchestrates ALL the cloud services in play; S3 for pushing code, API Gateway for handling HTTP requests, and of course Lambdas. But you don't have to worry about any of that- the tool takes care of these things behind the scenes.
Fastify Application
Let's build the app. Fast.
To get up and running, we will install fastify-cli and then generate the application.
1. To install fastify-cli use the command
npm i fastify-cli --global
2. To create a project template use the command:
fastify generate --lang=js fastify-lambda
-lang can be js or ts.
3. Install the dependencies
cd fastify-lambda
npm init
Let us understand the default code.
The app.js in the root folder contains the following code
'use strict'
const path = require('path')
const AutoLoad = require('@fastify/autoload')
const app = async function (fastify, opts) {
// This loads all plugins defined in plugins
// those should be support plugins that are reused
// through your application
fastify.register(AutoLoad, {
dir: path.join(__dirname, 'plugins'),
options: Object.assign({}, opts)
})
// This loads all plugins defined in routes
// define your routes in one of these
fastify.register(AutoLoad, {
dir: path.join(__dirname, 'routes'),
options: Object.assign({}, opts)
})
}
module.exports = app
This creates a default fastify app. Fastify.register is used to install a plugin.
Register the routes to the application by calling the function fastify.register with "routes"
as a
parameter.
Note: While in ExpressJS almost everything is a middleware in Fastify everything is a plugin
The default code in the file routes/root.js is as follows
module.exports = async function (fastify, opts) {
fastify.get('/', async function (request, reply) {
return { root: true }
})
}
This creates a default route and returns a JSON response { root : “true” }
To check the response on local dev environment use the command
npm run dev //starts the local dev environment
Add Custom Routes
Step 1 : Add default data
To keep the first project as simple as possible, let's avoid adding a database.
To replicate a database, let's create an items.js file and add some dummy data.
let items = [
{id: '1', tasks: "Eat"},
{id: '2', tasks: "Code"},
{id: '3', tasks: "Sleep"},
{id: '4', tasks: "Repeat"}
]
Step 2 : Create a controller file
1. Create a new folder controller to save all the controller file.
2. Create a ItemController.js file to add the controller code.
// get all the data from dummy database - items.js
let items = require('../items')
// Get all the items and send as response (reply)
const getItems = async (req, reply) => {
reply.send(items)
}
// Get individual item by id and send as response (reply)
const getItem = async (req, reply) => {
const { id } = req.params
const item = items.find((item) => item.id === id)
reply.send(item)
}
module.exports = {
getItems,
getItem,
}
Step 3 : Create routes and attach the controller to each route.
Modify the routes/root.js as follows
'use strict'
//Import the controller functions
const {
getItems,
getItem,
} = require('../controllers/ItemController')
// Schema for the generic data - each item has 2 fields - id and tasks.
// id and tasks will be of type string
const Item = {
type: 'object',
properties: {
id: {type: 'string'},
tasks: {type: 'string'},
}
}
// Schema for get all response
const getItemsOpts = {
schema: {
response: {
200: {
type: 'array',
items: Item
}
}
},
handler : getItems
}
// Schema for get unique item response
const getItemOpts = {
schema: {
response: {
200: Item
}
},
handler: getItem
}
module.exports = async function (fastify, opts) {
fastify.get('/', async function (request, reply) {
return { root: true }
})
fastify.get('/items', getItemsOpts)
fastify.get('/items/:id', getItemOpts)
}
Let us create 2 routes -
- To fetch all the tasks
- To fetch a task by its id
fastify.get('/items', getItemsOpts)
fastify.get('/items/:id', getItemOpts)
Each route is has a handler function or options as per Fastify ( or in general terms views in MVC
)
Example getItemsOpts and getItemOtps
const getItemsOpts = {
schema: {
response: {
200: {
type: 'array',
items: Item
}
}
},
handler : getItems
}
In the above code snippet, getItemsOpts option is defining a schema for the response. The Response contains an array of items and success status. And each element of array is an object of type Item [ schema ]
const Item = {
type: 'object',
properties: {
id: {type: 'string'},
tasks: {type: 'string'},
}
}
To check the response on local dev environment use the command
npm run dev //starts the local dev environment
127.0.0.1:3000/items will return the output
[{"id":"1","tasks":"Eat"},{"id":"2","tasks":"Code"},{"id":"3","tasks":"Sleep"},{"id":"4","tasks":"Repeat"}]
The AWS Serverless Application Model
The AWS Serverless Application Model (SAM) is an open-source framework for building serverless applications. It allows you to write a small YAML file to describe the function and the endpoints. And also with a simple command, the application is deployed to AWS Lambda and configured all the setup provisioning.
Basic SAM commands:
- sam build
- sam deploy to deploy the app to cloud
- sam validate to check the configuration file
- sam delete to delete the app from Lambda
Tools of Trade
Before digging into the world of serverless, let us first install the requisite software.
- Create or login to AWS account.
- Install AWS CLI
- Install AWS SAM
- Create an IAM user with Admin permissions (for practice)
- Configure AWS CLI using the command.
- Install Node, VS code (or any other IDE)
aws configure
AWS Access Key ID [None]: accesskey
AWS Secret Access Key [None]: secretkey
Default region name [None]: ap-south-1
Default output format [None]:
Steps to deploy the fastify app onto a lambda service
Step 1: Create a template.yaml and add the following code
AWSTemplateFormatVersion: "2010-09-09"
Transform: AWS::Serverless-2016-10-31
Description: >
FastifyApp
Sample SAM Template for app
# More info about Globals: https://github.com/awslabs/serverless-application-model/blob/master/docs/globals.rst
Globals:
Function:
Timeout: 3
Resources:
FastifyApp:
Type: AWS::Serverless::Function
Properties:
Handler: dist/lambda.handler
PackageType: Zip
Runtime: nodejs14.x
MemorySize: 128
Timeout: 500
Events:
AppEventAPI:
Type: HttpApi
Properties:
Path: /{proxy+}
Method: any
Outputs:
# ServerlessRestApi is an implicit API created out of Events key under Serverless::Function
# Find out more about other implicit resources you can reference within SAM
# https://github.com/awslabs/serverless-application-model/blob/master/docs/internals/generated_resources.rst#api
AppEventAPI:
Description: "API Gateway endpoint URL for Prod stage for Hello World function"
Value: !Sub "https://${ServerlessHttpApi}.execute-api.${AWS::Region}.amazonaws.com/"
FastifyApp:
Description: "Hello World Lambda Function ARN"
Value: !GetAtt FastifyApp.Arn
FastifyAppIamRole:
Description: "Implicit IAM Role created for Hello World function"
Value: !GetAtt FastifyAppRole.Arn
The above file is a config file for the CloudFormation service. This will create a project in CloudFormaton with a Lambda function, and API gateway trigger attached to it.
Convert the app to Lambda
To get the events and all the data from AWS Gateway in our app we need an extra layer. Luckily we don't have to write it from scratch because there is a library available for this purpose. It's called aws-lambda-fastify. Let's install it and use it.
npm i aws-lambda-fastify
Now we need to wrap our app with the library.
Create a lambda.js file in the root directory and add the following code.
const awsLambdaFastify = require("aws-lambda-fastify");
import "./app.js";
const server = fastify({logger: true})
const proxy = awsLambdaFastify(server);
exports.handler = proxy;
Congratulations we made it. Now, all we have to do is build the app and deploy it using the SAM CLI:
sam build
sam deploy
To hire fastify developers, contact us at contact@bluetickconsultants.com