Hopp til hovedinnhold

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!

Did you like the post?

Feel free to share it with friends and colleagues