Skip to main content

Best Serverless Frameworks for Node.js in 2026

·PkgPulse Team

TL;DR

SST for full-stack TypeScript on AWS; Serverless Framework for multi-cloud legacy deployments. SST (~200K weekly downloads) is the modern TypeScript-first framework that deploys to AWS with live function development, type-safe resource binding, and a growing ecosystem. Serverless Framework (~2M downloads) is the older multi-provider tool with thousands of plugins. AWS CDK (~500K) is AWS's official infrastructure-as-code with full TypeScript. For new AWS-native apps in 2026, SST is the compelling choice.

Key Takeaways

  • Serverless Framework: ~2M weekly downloads — multi-provider, 1K+ plugins, widely deployed
  • SST: ~200K downloads — TypeScript-first, live Lambda dev, resource binding, AWS-native
  • AWS CDK: ~500K downloads — low-level AWS infrastructure, TypeScript/Python/Java
  • SST v3 — Ion release uses Pulumi under the hood, faster deployments
  • Local development — SST live lambda lets you test against real AWS services instantly

Serverless Framework

# serverless.yml — multi-provider configuration
service: my-api

provider:
  name: aws
  runtime: nodejs20.x
  region: us-east-1
  stage: ${opt:stage, 'dev'}
  environment:
    DATABASE_URL: ${ssm:/myapp/${self:provider.stage}/database-url}
  iam:
    role:
      statements:
        - Effect: Allow
          Action:
            - dynamodb:GetItem
            - dynamodb:PutItem
            - dynamodb:DeleteItem
          Resource: !GetAtt UsersTable.Arn

functions:
  createUser:
    handler: src/users/create.handler
    events:
      - httpApi:
          path: /users
          method: POST
  getUser:
    handler: src/users/get.handler
    events:
      - httpApi:
          path: /users/{id}
          method: GET
  processQueue:
    handler: src/queue/processor.handler
    events:
      - sqs:
          arn: !GetAtt ProcessingQueue.Arn
          batchSize: 10

resources:
  Resources:
    UsersTable:
      Type: AWS::DynamoDB::Table
      Properties:
        TableName: ${self:service}-${self:provider.stage}-users
        BillingMode: PAY_PER_REQUEST
        AttributeDefinitions:
          - AttributeName: id
            AttributeType: S
        KeySchema:
          - AttributeName: id
            KeyType: HASH

plugins:
  - serverless-offline          # Local dev
  - serverless-esbuild          # TypeScript build
  - serverless-prune-plugin     # Clean old deployments
// src/users/create.ts — Lambda handler
import type { APIGatewayProxyHandler } from 'aws-lambda';
import { DynamoDBClient, PutItemCommand } from '@aws-sdk/client-dynamodb';
import { z } from 'zod';

const db = new DynamoDBClient({});

const schema = z.object({
  name: z.string(),
  email: z.string().email(),
});

export const handler: APIGatewayProxyHandler = async (event) => {
  try {
    const body = schema.parse(JSON.parse(event.body ?? '{}'));

    await db.send(new PutItemCommand({
      TableName: process.env.USERS_TABLE,
      Item: {
        id: { S: crypto.randomUUID() },
        name: { S: body.name },
        email: { S: body.email },
        createdAt: { S: new Date().toISOString() },
      },
    }));

    return { statusCode: 201, body: JSON.stringify({ success: true }) };
  } catch (err) {
    return { statusCode: 400, body: JSON.stringify({ error: String(err) }) };
  }
};

SST v3 (Modern AWS)

// sst.config.ts — TypeScript infrastructure
import { SSTConfig } from 'sst';
import { Api, Table, Bucket, NextjsSite, Cron } from 'sst/constructs';

export default {
  config(input) {
    return {
      name: 'my-app',
      region: 'us-east-1',
    };
  },
  stacks(app) {
    app.stack(function API({ stack }) {
      const table = new Table(stack, 'Users', {
        fields: { id: 'string', email: 'string' },
        primaryIndex: { partitionKey: 'id' },
        globalIndexes: {
          EmailIndex: { partitionKey: 'email' },
        },
      });

      const bucket = new Bucket(stack, 'Uploads');

      const api = new Api(stack, 'Api', {
        routes: {
          'POST /users': 'packages/functions/src/users/create.handler',
          'GET /users/{id}': 'packages/functions/src/users/get.handler',
          'POST /upload': 'packages/functions/src/upload.handler',
        },
        // Type-safe resource binding!
        bind: [table, bucket],
      });

      // Next.js site with SSR
      const site = new NextjsSite(stack, 'Web', {
        path: 'packages/web',
        bind: [api, table],
      });

      // Cron job
      new Cron(stack, 'Cleanup', {
        schedule: 'rate(1 day)',
        job: { function: 'packages/functions/src/cleanup.handler' },
      });

      stack.addOutputs({
        ApiUrl: api.url,
        SiteUrl: site.url,
      });
    });
  },
} satisfies SSTConfig;
// SST resource binding — type-safe, no env vars needed
// packages/functions/src/users/create.ts
import { Table } from 'sst/node/table';
import { Bucket } from 'sst/node/bucket';
import { DynamoDBClient, PutItemCommand } from '@aws-sdk/client-dynamodb';
import { S3Client, PutObjectCommand } from '@aws-sdk/client-s3';

// SST injects the correct table name/bucket name at runtime
// Table.Users.tableName — automatically bound
// Bucket.Uploads.bucketName — automatically bound
const db = new DynamoDBClient({});

export const handler = async (event) => {
  await db.send(new PutItemCommand({
    TableName: Table.Users.tableName,  // Type-safe, no process.env string
    Item: { /* ... */ },
  }));
};
# SST commands
npx sst dev              # Start live Lambda dev (changes deploy instantly)
npx sst deploy           # Deploy to AWS
npx sst deploy --stage prod  # Deploy to production
npx sst remove           # Tear down all resources
npx sst console          # Open SST Console (logs, DynamoDB viewer, etc.)

When to Choose

ScenarioPick
New AWS project, TypeScriptSST
Multi-cloud (AWS + GCP + Azure)Serverless Framework
Complex AWS infrastructureAWS CDK
Legacy Serverless Framework projectKeep SF (migration not worth it)
Full-stack (Lambda + Next.js + DynamoDB)SST
Need Serverless ConsoleServerless Framework
Fine-grained AWS controlAWS CDK

Compare serverless framework package health on PkgPulse.

Comments

Stay Updated

Get the latest package insights, npm trends, and tooling tips delivered to your inbox.