Serverless Framework vs AWS CDK: A Hands-On Comparison

Serverless Framework vs AWS CDK
author valts

by Valts Ausmanis · January 16, 2024

Nowadays when we talk about building and deploying cloud applications - Infrastructure as code (IaC) term is no stranger to us. Most of the cloud applications are being managed and deployed through the “code” instead of manual changes. In this article we will take a look on two widely used IaC tools: Serverless Framework and Cloud Development Kit (CDK). Both tools are open-source and built with focus to deploy applications on AWS cloud. Based on the Datadog “State of serverless” report – Serverless Framework is the most popular IaC tool used for managing AWS Lambda functions among Datadog customers. But let’s take a step back because most popular doesn’t always mean the best fit for everything and take a look on what it takes to build and deploy serverless application using both of these tools.

In This Article

History

For several years Serverless Framework (starting from 2016) was the go-to tool for building and deploying serverless (mainly AWS Lambda based) applications on AWS cloud. Yes, there was AWS SAM (available from 2016) but due to some missing functionality (like custom authorizers, no real CLI support at that time etc.), being more like AWS CloudFormation extension not a real IaC tool and of course Serverless Framework awesome community at that time it wasn’t the first choice.

But then in 2019 AWS CDK was released which was developed by AWS - finally the first real Infrastructure as Code tool where you could use your preferred programming language (like Typescript, Python) for building and deploying cloud applications to AWS.

Let’s Build a Serverless Application

The best way to compare these two tools is to have real hands-on comparison and take a simple serverless application use case like “Newsletter back-end service” with following resources to be built and deployed on AWS cloud:

  • API Gateway with two endpoints:
    • Add new subscriber POST /subscriber
    • Delete subscriber DELETE /subscriber/{subscriberId}
  • Two Lambda functions to add and delete our newsletter subscribers
  • DynamoDB to store the subscriber data

Additionally, we will use code bundler to bundle and minify our Lambda JavaScript code. In this case we will stick with esbuild which have proved itself as pretty fast one.

This way we will be able to see what it takes to build and deploy this application to AWS from both sides.

What Is Serverless Framework?

Serverless Framework is an open-source Infrastructure as Code (IaC) tool for building, packaging and deploying serverless application to major public clouds like AWS, Microsoft Azure, Google Cloud Platform and more.

How Do You Define Serverless Application In Serverless Framework?

Serverless applications and related resources (like functions, events, roles etc.) are being configured via YAML file serverless.yml:


service: newsletter

functions: # Your "Functions"
  subscribe:
    handler: src/subscribe.handler
    events: # The "Events" that trigger this function
      - httpApi: 'POST /subscriber'

  unsubscribe:
    handler: src/unsubscribe.handler
    events:
      - httpApi: 'DELETE /subscriber/{subscriberId}'

We have now defined Lambda functions and added API Gateway as event source to trigger Lambdas but we still have to add DynamoDB table and give Lambdas access rights to update DB table:


custom:
  SUBSCRIBERS_TABLE: ${self:service}-${self:provider.stage}-subscribers

provider:
  iam:
    role:
      statements:
        - Effect: Allow
          Action:
            - dynamodb:UpdateItem
            - dynamodb:DeleteItem
          Resource:
            - Fn::GetAtt: [SubscribersTable, Arn]

# The "Resources" your "Functions" use. Raw AWS CloudFormation goes in here.
resources:
  Resources:
    SubscribersTable:
      Type: AWS::DynamoDB::Table
      Properties:
        TableName: ${self:custom.SUBSCRIBERS_TABLE}
        AttributeDefinitions:
          - AttributeName: subscriberId
            AttributeType: S
        KeySchema:
          - AttributeName: subscriberId
            KeyType: HASH
        BillingMode: PAY_PER_REQUEST

If due to some reason YAML format doesn’t work for you it’s possible to use alternative configuration formats like JSON, JavaScript or TypeScript. But don’t get too excited about JS or TypeScript usage – at the end it’s still the JS object that should be exported.

Packaging And Deployment With Serverless Framework

Before we can package our serverless framework service we have to set provider values where to deploy (= AWS), Lambda runtime, default stage and region in serverless.yml:


provider:
  name: aws
  runtime: nodejs18.x
  stage: ${opt:stage, 'dev'}
  region: ${opt:region, 'eu-west-1'}

If necessary your can check out serveless.yml reference with all the available properties when provider is set to aws.

To decrease the Lambda code size (and to improve AWS lambda cold starts) we will package our functions separately and use esbuild bundler by adding following lines in the serverless.yml:


custom:
  esbuild:
    exclude: ['@aws-sdk'] # Exclude the @aws-sdk from the build as it is already available in the Lambda environment
    minify: true # Minify the code

package:
  individually: true  # Package individually per functions

You can find complete serverless.yml code for our newsletter serverless application in our github.com/cloudviz-io/cloudviz-blog-samples repository.

By using Serverless CLI, it’s possible to package your serverless project without deploying it to AWS. Running the serverless package command Serverless Framework will build the deployment artifacts (for default “dev” stage) in .serverless directory:serverless framework package artifacts

Artifacts folder contains:

  • Zip file containing code for each Lambda function for this serverless service
  • CloudFormation template that describes resources defined in serverless.yml
  • Serverless Framework internally used servereless-state.json file

You can package and deploy multiple similar serverless applications by just using the --stage <stage-name> parameter. This is pretty useful in CI/CD workflows when deploying your application to dev -> qa -> prod environments. By using different stage names for your environments, the underlying serverless application resources like Lambda functions, API gateway endpoints, Cloudwatch log groups etc. are auto-named in a nice, clear way. Example for Lambda functions: <service-name>-<stage>-<function-name> -> "newsletter-dev-subscribe"

To deploy serverless application just use simple CLI command serverless deploy, which will deploy application to AWS cloud by using configured default AWS credentials:serverless application deployment with serverless framework

We have successfully deployed serverless application with two API Gateway endpoints, Lambdas and DynamoDB table to AWS cloud in less than 2 minutes. Additionally, S3 bucket was created where the Serverless Framework uploads application artifacts (zipped Lambda code, CloudFormation templates) and uses these for deployment process. Now to see the overview of our newsletter service let's visualize deployed serverless architecture with Cloudviz.io AWS diagram generator:

You can find all the IaC and Lambda related code for building newsletter service using Serverless Framework in our GitHub repository.

What Is AWS Cloud Development Kit (CDK)?

AWS CDK is an open-source Infrastructure as Code (IaC) tool for building, packaging and deploying cloud applications to AWS using your preferred programming language like TypeScript, JavaScript, Python, Java etc.

How Do You Define Serverless Application In AWS CDK?

In order to define AWS infrastructure for serverless application we have to create AWS CDK app which is written in your preferred programming language (ex. TypeScript, JavaScript, Python etc.) that uses the AWS CDK library.

Each CDK app consists of:

  • App represents root object and can contain multiple stacks. An app represents all the stacks that application requires to be fully functional.
  • Stack represents an AWS CloudFormation template with all the related AWS resources. Stacks can be deployed separately.
  • Constructs represents AWS resources that we will create (like. Lambda, API Gateway, DynamoDB etc.).

AWS suggested way of defining CDK app is to have:

  • bin folder that contains file which is main entry point for our application (newsletter.js).
  • lib folder that contains all code for CDK stacks and constructs to define AWS resources that should be deployed (newsletter-stack.js).
aws cdk serverless app structure

bin/newsletter.js - JavaScript code that defines main entry point for cloud application:


import { App } from 'aws-cdk-lib';
import { NewsletterCdkStack } from '../lib/newsletter-stack.js';
import { resourceName } from './app-config.js';

const app = new App();

// See more about defining environments: https://docs.aws.amazon.com/cdk/v2/guide/environments.html
new NewsletterCdkStack(app, resourceName('demo'), {  stage: 'demo', env: { region: 'eu-west-1' } });

lib/newsletter-stack.js - JavaScript code that defines AWS resources for our newsletter serverless application:


import { Stack } from 'aws-cdk-lib';
import { resourceName } from '../bin/app-config.js';
import { Runtime } from 'aws-cdk-lib/aws-lambda';
import { NodejsFunction } from 'aws-cdk-lib/aws-lambda-nodejs';
import { LambdaIntegration, RestApi } from 'aws-cdk-lib/aws-apigateway';
import { Effect, PolicyStatement } from 'aws-cdk-lib/aws-iam';
import { Table, AttributeType } from 'aws-cdk-lib/aws-dynamodb';

export class NewsletterCdkStack extends Stack {
  constructor(scope, id, props) {
    super(scope, id, props);

    // create subscriber DynamoDB table
    const subscriberTable = new Table(this, 'SubscriberTable', {
      tableName: resourceName(`${props.stage}-subscribers`),
      partitionKey: {
        name: 'subscriberId',
        type: AttributeType.STRING,
      },
    });

    // set default Lambda parameters
    const nodeJsFunctionProps = {
      bundling: {
        externalModules: [
          '@aws-sdk', // Use the '@aws-sdk' available in the Lambda runtime
        ],
        minify: true,
      },
      runtime: Runtime.NODEJS_18_X,
      initialPolicy: [
        new PolicyStatement({
          actions: ['dynamodb:DeleteItem', 'dynamodb:UpdateItem'],
          effect: Effect.ALLOW,
          resources: [subscriberTable.tableArn],
        }),
      ],
      environment: {
        SUBSCRIBERS_TABLE: subscriberTable.tableName,
      },
    };

    // create Lambda functions
    const subscribeLambda = new NodejsFunction(this, 'SubscribeHandler', {
      functionName: resourceName(`${props.stage}-subscribe`),
      entry: 'src/subscribe.js',
      ...nodeJsFunctionProps,
    });

    const unsubscribeLambda = new NodejsFunction(this, 'UnsubscribeHandler', {
      functionName: resourceName(`${props.stage}-unsubscribe`),
      entry: 'src/unsubscribe.js',
      ...nodeJsFunctionProps,
    });

    // create API Gateway endpoints
    const api = new RestApi(this, 'NewsletterApi', {
      restApiName: resourceName(`${props.stage}-api`),
      endpointExportName: resourceName(`${props.stage}-ApiGwUrl`),
      description: 'Newsletter API',
      deployOptions: { stageName: props.stage },
    });

    // attach Lambda functions to API Gateway endpoints
    const subscriberEndpoint = api.root.addResource('subscriber');
    subscriberEndpoint.addMethod('POST', new LambdaIntegration(subscribeLambda));

    const unsubscribeEndpoint = subscriberEndpoint.addResource('{subscriberId}');
    unsubscribeEndpoint.addMethod('DELETE', new LambdaIntegration(unsubscribeLambda));
  }
}

Packaging And Deployment With AWS CDK

By using CDK CLI, it’s possible to package your serverless project without deploying it to AWS. This can be useful in CI/CD workflows to verify that your IaC code can successfully generate the build and use the same build artifacts (like Lambda code, templates) when deploying across all the environments (ex. dev -> qa -> prod). Running the cdk synth command CDK will build the deployment artifacts (for default “demo” stage) in cdk.out directory:

aws cdk app packaged assets

Artifacts folder contains:

  • Asset files containing code for each Lambda function for our newsletter serverless service
  • CloudFormation template (newesletter-demo.template.json) that describes AWS resources defined in CDK stack
  • tree.json file which is used by AWS CDK Explorer to display your resources in a tree view
  • manifest.json file that defines the set of instructions that are needed in order to deploy the assembly directory

Lambda code was bundled using esbuild.

Before we can deploy our CDK app we have to first bootstrap (with command cdk bootstrap) specific AWS account and region. The main purpose is to provision set of resources (like S3 bucket, IAM roles) that are required to support deployment of CDK applications.cdk app bootstrap

To deploy serverless application just use simple CLI command cdk deploy, which will deploy application to AWS cloud by using configured default AWS credentials:

aws cdk app deployment

You can find all the IaC and Lambda related code for newsletter serverless application using AWS CDK in our GitHub repository.

What Are The Differences Between Serverless Framework And AWS CDK?

So we have built and deployed two similar serverless applications using two different IaC tools: Serverless Framework and AWS CDK. As you can already see in previous Lets build serverless application section that there are differences how you build, define, package and deploy serverless applications to AWS cloud. Now let’s dive into the more specific comparison.

Templates (YAML, JS) vs Real Code (Typescript, Python etc.)

Serverless Framework lets you define your serverless application by using templates (YAML or JavaScript object). AWS CDK lets you use your preferred programming language (TypeScript, JavaScript, Python, Java, C# or Go) to define your next cloud application.

Some will say that they like simplicity of Serverless Framework and I fully agree with them until they start to grow their application and add AWS resources that are not part of default serverless.yml template or available as plugins. When you start to add custom CloudFormation templates to your project the complexity of serverless application starts to grow and you have to get your hands dirty by using purely CloudFormation templates, keep the common AWS resource naming, properly tag your resources, and add different IAM access rights to your Lambdas or other resources that are involved. However, by using CDK and its constructs you have most of the AWS resources already defined as part of the AWS CDK library and ready to be used in your cloud application.

Open-Source Community: Serverless Framework 46k Stars vs CDK 11k Stars

When we compare the popularity of these two tools in open-source community there is clear winner – Serverless Framework with ~46k stars comparing to CDK ~11k stars.

But it’s not a big surprise as Serverless Framework was released 3 years earlier than CDK and many organizations are still using it because at that time there wasn’t any good alternative and it still works really well, being well maintained and there is almost 99% chance that when you will hit any issue there will be already answer for that because someone had the same challenge as you before.

Multi-Cloud vs AWS Only

You can use Serverless Framework for not only to deploy your serverless application to AWS but to other major public clouds like Microsoft Azure or Google Cloud Platform.

This is really nice but you should ask a question to yourself: “Will I really deploy my serverless application to multiple cloud providers?”. In most scenarios your answer will be “No”. If the answer due to some specific requirements of your use case is “Yes”, then it’s clear that the complexity of your serverless application (like traffic routing, load balancing etc.) will grow and as we talked before the main advantage of Serverless Framework is its simplicity. Thus, for multi-cloud deployments I would consider different IaC tools like Terraform or even Pulumi that will let you to go way above the simple serverless application requirements and provision any additional resources (like DNS stuff, Load balancers etc.) needed to run your cloud application.

Simplicity vs Bit More Complexity And Learning Curve

For serverless you basically have one definition serverless.yml template which you use to define your serverless application and additional CloudFormation templates for the AWS resources that can’t be defined using default Serverless Framework parameters or available as plugins.

For CDK you could have one input file too but that brakes some of the advantages of using your preferred programming language to structure your IaC code properly. That’s why I would suggest that you first go through some CDK “hello world” example docs to better understand how to structure and define your CDK application and how to correctly create the stacks and constructs. This adds a bit more learning curve to CDK when compared with the Serverless Framework.

Connecting Your Lambdas To VPC

Based on the Datadog “State of serverless” report – 65% of organizations have connected AWS Lambda functions to a VPC. When you connect your Lambdas to VPC you improve security for your serverless application so that Lambdas can “talk” to other AWS services like S3, DynamoDB, RDS without requests actually leaving the AWS network (ex. by using VPC endpoints or proper security group configurations). But for many organizations it’s not only about additional security but the fact that they have already solid compute and database workloads running in the VPCs and they connect Lambdas to VPC in order to integrate with existing databases, cache instances or even internal services.

For serverless framework you can use serverless VPC plugin to automatically create new VPC related resources based on your provided configuration in serverless.yaml file. Or use serverless VPC discovery plugin to setup the existing vpc configuration for Lambdas.

For CDK you can use VPC construct to define your new VPC resources or use VPC lookup function to get the details of existing VPC and use it when defining your Lambdas.

Both tools provide several default values for new VPC creation that you “kinda don’t have to think about” and would work for common use-cases. But to be honest I would not use Serverless Framework to deploy completely new VPC. Why? You can ask yourself a question – “Why am I deploying new VPC? Will I use additional VPC based services like RDS database, cache instances or maybe I will add VPC peering to existing VPC to connect to existing databases?”. If you answer to second question is “Yes” then most likely you will have to use custom CloudFormation templates to make this work with Serverless Framework and then at the end you will use serverless.yml + serverless vpc plugin + CloudFormation templates which is far from term “simplicity”.

Serverless Plugins vs CDK Constructs

There are many Serverless Framework plugins that can extend or overwrite frameworks default functionality. Most of the plugins are maintained by different community members.

For CDK the most similar thing to Serverless Framework plugins could be CDK construct library. L2 constructs represent specific AWS resources and L3 constructs represent patterns to help you complete common tasks in AWS, often involving multiple kinds of resources (ex. API Gateway REST API with AWS Lambda proxy integration).

The main differences between Serverless Framework plugins and AWS CDK Constructs:

  • Most of the Serverless Framework plugins are third party / community maintained separately from serverless repository. However, CDK constructs are being kept and maintained in the main aws-cdk repository
  • Serverless Framework plugins are mainly to be used for building serverless applications (and that’s ok) but CDK constructs can be used to build any cloud application for AWS cloud.

Local Development

Serverless Framework CLI includes commands to test and invoke your functions locally by using command like serverless invoke local --function functionName.

AWS CDK CLI doesn’t have this functionality included but it’s possible to use SAM CLI to locally invoke the CDK app functions, start APIs and Lambda endpoints:


# Invoke the function FUNCTION_IDENTIFIER declared in the stack STACK_NAME
sam local invoke [OPTIONS] [STACK_NAME/FUNCTION_IDENTIFIER]

# Start all APIs declared in the AWS CDK application
sam local start-api -t ./cdk.out/CdkSamExampleStack.template.json [OPTIONS]

# Start a local endpoint that emulates AWS Lambda
sam local start-lambda -t ./cdk.out/CdkSamExampleStack.template.json [OPTIONS]

What Are The Similarities Between Serverless Framework And AWS CDK?

Both Are AWS Focused

Even Serverless Framework has multiple cloud providers which you can use to deploy your serverless applications – AWS is still default cloud provider used by Serverless Framework and therefore all the Serverless Framework documentation applies to AWS by default.

AWS CDK was developed by AWS and is meant to be used to deploy your cloud applications to AWS cloud only.

Under The Hood It’s Still CloudFormation

Comparing with other IaC tools like Terraform which uses cloud provider API calls and manages the state of the application by itself (which sometimes causes a lot of pain for developers) to deploy cloud applications to AWS – both Serverless Framework and AWS CDK uses AWS IaC service called AWS CloudFormation.

Packaging Speed

In the Lets build serverless application section we generated serverless application deployment assets (CloudFormation templates, bundled Lambda code) by using both of the tools. To be correct we used the same code bundler esbuild for both tools. The build results were basically the same.

Serverless packaging:serverless framework packaging time

CDK packaging:aws cdk app packaging time

As you can see Serverless Framework packaged serverless application in 0s and CDK in 11ms (0.011s). As most likely Serverless Framework rounded the ms to whole seconds the result was the same.

Deployment Speed

Both tools under the hood use AWS CloudFormation to deploy cloud applications to AWS. So, deployment speed per serverless application deployment (= one CloudFormation stack) should be similar.

In previous sections when we built and deployed our newsletter serverless application by using both of the tools deployment speeds bit differed (~20 seconds difference).

Serverless Framework deployment:serverless framework deployment time

CDK deployment:aws cdk app deployment time

One assumption for this difference could be because underlying Lambda, API Gateway, DynamoDB and IAM services provided different response times for similar requests during new AWS resource creation.

So Which One Should I Choose?

I would suggest to choose the IaC tool for your next project based on the complexity of your infrastructure and already existing knowledge inside the team. For example, if your team has already solid Terraform experience and if you are successfully using it for your existing cloud applications then in most scenarios it wouldn’t make sense to start to use CDK or Serverless Framework at all.

If your team wants to explore new IaC tools for the next project then analyze complexity of the cloud application that you will build. I have mentioned and showed in this article Serverless Framework simplicity when it comes to quickly build simple serverless applications with Lambdas and different event sources. If you see that you will not use additional VPC based services like RDS databases, cache instances, VPC endpoints, VPC peering etc. and just use Lambdas with few other AWS resources like API GW, S3 or DynamoDB then you should be good by choosing Serverless Framework.

Before AWS CDK was released, I was all-in Serverless Framework advocate for building and deploying serverless applications to AWS cloud. At first, I didn’t welcome this new tool with open arms. I was so used to Serverless Framework simplicity and community support that I unintentionally ignored use cases (like Lambdas connected to custom VPCs and accessing dedicated RDS databases and other VPC related services) where it could make sense to use the AWS CDK fully. But instead, we used Serverless Framework for purely serverless based services containing Lambdas, API GW, SNS, DynamoDB etc. and CDK for VPC related resources like custom VPCs, security groups, RDS databases, VPC peering, NAT GWs, VPC endpoints etc. After some time, I and our team got used to AWS CDK and for next projects we started to use this tool fully.

Now few years later I still think this was good decision! You use one IaC tool and have all your infrastructure code (no custom CloudFormation templates + plugins + serverless.yml combination) and your Lambdas code written in one language (like JavaScript, TypeScript, Python etc.). Yes, there will be small learning curve comparing to Serverless Framework but If you and your team already use one of the CDK supported languages (and most likely you do) then your first CDK application will be up and running in no time.



Tired of browsing through the AWS console?

Try out Cloudviz.io and visualize your AWS cloud environment in seconds

Start your free trial

As experienced AWS architects and developers, our goal is to provide users an easy way to create stunning AWS architecture diagrams and detailed technical documentation. Join us to simplify your diagramming process and unleash the beauty of your cloud infrastructure


Copyright © 2019 - 2024 Cloudviz Solutions SIA