If you've read yesterdays post about ZEIT Now, you've already seen one of many approaches to develop and deploy a serverless API. In this post we will have a look at an alternative you can use to develop serverless applications, namely Serverless Framework. Serverless Framework can be used to develop, deploy and test your serverless applications targeted towards different cloud providers, or as they describe themselves: "The complete solution for building & operating serverless applications." Let's have a look on what this framework is all about.
Serverless Framework is a node CLI tool which can be installed via npm by running npm install -g serverless
. As mentioned the framework can be used against many different cloud providers. AWS, Azure and Google is supported, but also more "exotic" providers like Cloudflare Workers, OpenWhisk and Alibaba Cloud. In this post we will focus on how to use Serverless Framework on AWS.
Serverless Framework concepts
Serverless Framework has four main concepts which you should know before we look on some example code.
Service
You can think of this as an application or as a ... yeah, service. It typically consists of multiple functions (Lambda in our case) and different resources as databases, S3 buckets, API gateway and other managed services.
Functions
On AWS this is equivalent to AWS Lambda. A single piece of code deployed to AWS which triggers based on different kinds of events in the cloud ecosystem. In your service you can define multiple functions, and you can deploy and invoke them via the Serverless Framework CLI.
Events
Events are what trigger your functions you have defined in your service. On AWS this can be anything from a HTTP request from API Gateway, an image uploaded to a S3 bucket, a new entry in a DynamoDB table, a CloudWatch alert and lots more.
Resources
These are any resources which is needed by your service, and typically what your functions use. Typical resources may be a database like DynamoDB , S3 bucket or a SNS topic.
What does this look like in practice?
Below is an example of a service defined in a file called serverless.yml
. The service is an API which you can use to create new christmas wishes, and return a list of all your wishes.
service: wishlist
provider:
name: aws
runtime: nodejs12.x
region: eu-central-1
iamRoleStatements:
- Effect: "Allow"
Action:
- dynamodb:Scan
- dynamodb:PutItem
Resource: "arn:aws:dynamodb:eu-central-1:*:*"
functions:
wishlist:
handler: wishlist.wishlist
events:
- http: GET wishes
newWish:
handler: newWish.newWish
events:
- http: POST wishes
resources:
Resources:
WishlistTable:
Type: AWS::DynamoDB::Table
Properties:
TableName: wishlist
AttributeDefinitions:
- AttributeName: id
AttributeType: S
- AttributeName: wish
AttributeType: S
KeySchema:
- AttributeName: id
KeyType: HASH
- AttributeName: wish
KeyType: RANGE
ProvisionedThroughput:
ReadCapacityUnits: 1
WriteCapacityUnits: 1
We start by defining the name for our service, wishlist
. Then we define which provider we would like to use, aws
, runtime for our functions, nodejs
, and which region we would like our service to be deployed to, eu-central-1
. The iamRoleStatements
declares that our functions are allowed to query and add items to our DynamoDB table. The last block, resources
, defines our DynamoDB table where our wishes are stored.
The functions block could do with a more detailed explanation.
functions:
wishlist:
handler: wishlist.wishlist
events:
- http: GET wishes
newWish:
handler: newWish.newWish
events:
- http: POST wishes
Here we declare two functions. The wishlist
function is a function that returns all wishes from our DynamoDB table. In the events
block we define that this should trigger on a http GET request with the path /wishes
. The newWish
function will, as the name applies, create a new wish in our DynamoDB table. It triggers on a http POST request with the path /wishes
. The actual code for the two functions is listed below.
List all wishes
const AWS = require("aws-sdk");
const dynamoDb = new AWS.DynamoDB.DocumentClient();
module.exports.wishlist = async () => {
try {
const result = await dynamoDb.scan({ TableName: "wishlist" }).promise();
return {
statusCode: 200,
body: JSON.stringify(result),
};
} catch (e) {
return {
statusCode: 500,
body: e.message,
};
}
};
Add new wish
const AWS = require("aws-sdk");
const dynamoDb = new AWS.DynamoDB.DocumentClient();
module.exports.newWish = async (event) => {
try {
const result = await dynamoDb
.put({ TableName: "wishlist", Item: JSON.parse(event.body) })
.promise();
return {
statusCode: 200,
body: JSON.stringify(result),
};
} catch (e) {
return {
statusCode: 500,
body: e.message,
};
}
};
So, how do we actually deploy our service to the cloud?
To deploy our service just run the command sls deploy
. Serverless Framework will deploy all your functions and necessary infrastructure, and print the URL for your brand new serverless API at the bottom.
$ sls deploy
.......
Serverless: Stack update finished...
Service Information
service: wishlist
stage: dev
region: eu-central-1
stack: wishlist-dev
resources: 17
api keys:
None
endpoints:
GET - https://ny8fxydfd5.execute-api.eu-central-1.amazonaws.com/dev/wishes
POST - https://ny8fxydfd5.execute-api.eu-central-1.amazonaws.com/dev/wishes
functions:
wishlist: wishlist-dev-wishlist
newWish: wishlist-dev-newWish
layers:
None
So what actually happend here? Under the hood, Serverless Framework creates a CloudFormation template and uploads that to AWS. CloudFormation will take this template and provision all of the necessary services, deploy the functions and link all the services nicely together. In our case the framework created a DynamoDB table, two functions and an API Gateway.
With just a small yaml file, two functions and some shell commands, you suddenly have an API which autoscales automatically and you only pay when the API is in use. I think that's pretty cool!
What's next?
This was only a simple example of what Serverless Framework can do for you when developing serverless applications. In addition to create more complex applications, the framework can be used to test your functions locally or trigger them directly in the cloud, and you can use the framework for accessing logs and other metrics. Serverless Framework also comes with a Dashboard which gives you a "A unified view of your Serverless applications, featuring monitoring, alerting, deployments & much more".
All in all, Serverless Framework is a nice alternative to use for developing serverless applications. It supports multiple cloud providers (although the support is limited on some of them) and comes with lots of plugins to help you speed up your development. It has some limitations, and yaml
can be frustrating to debug some times, but I will absolutely recommend you to try it out if you're interested in developing serverless applications!