This article was originally published on AI Study Room. For the full version with working code examples and related articles, visit the original post.
Serverless Framework: From Zero to Production
Serverless Framework: From Zero to Production
Introduction
The Serverless Framework provides a unified experience for deploying functions, APIs, and event-driven architectures across major cloud providers. While serverless eliminates infrastructure management, it introduces challenges around cold starts, observability, and cost control. This guide walks through taking a serverless application from development to production using the Serverless Framework on AWS Lambda.
Project Setup and Structure
A well-structured serverless project separates concerns across functions, layers, and configuration:
serverless.yml
service: order-processor
frameworkVersion: "4"
provider:
name: aws
runtime: nodejs20.x
region: us-east-1
stage: ${opt:stage, 'dev'}
environment:
ORDER_TABLE: ${self:custom.tableName}
QUEUE_URL: !Ref OrderQueue
plugins:
- serverless-webpack
- serverless-offline
- serverless-prune-plugin
custom:
tableName: orders-${self:provider.stage}
webpack:
packager: pnpm
excludeFiles: src/*/.test.ts
prune:
automatic: true
number: 3
functions:
createOrder:
handler: src/handlers/createOrder.handler
events:
- httpApi:
method: POST
path: /orders
timeout: 10
memorySize: 256
iamRoleStatements:
- Effect: Allow
Action: dynamodb:PutItem
Resource: !GetAtt OrdersTable.Arn
Infrastructure as Code
Define resources alongside functions for self-documenting infrastructure:
resources:
Resources:
OrdersTable:
Type: AWS::DynamoDB::Table
Properties:
TableName: orders-${self:provider.stage}
BillingMode: PAY_PER_REQUEST
AttributeDefinitions:
- AttributeName: orderId
AttributeType: S
- AttributeName: status
AttributeType: S
KeySchema:
- AttributeName: orderId
KeyType: HASH
GlobalSecondaryIndexes:
- IndexName: StatusIndex
KeySchema:
- AttributeName: status
KeyType: HASH
Projection:
ProjectionType: ALL
OrderQueue:
Type: AWS::SQS::Queue
Properties:
QueueName: orders-${self:provider.stage}
VisibilityTimeout: 60
RedrivePolicy:
deadLetterTargetArn: !GetAtt DeadLetterQueue.Arn
maxReceiveCount: 3
DeadLetterQueue:
Type: AWS::SQS::Queue
Properties:
QueueName: orders-dlq-${self:provider.stage}
Lambda Handler Implementation
Write handlers with proper error handling and observability:
// src/handlers/createOrder.ts
import { APIGatewayProxyEvent, APIGatewayProxyResult } from "aws-lambda";
import { DynamoDBClient } from "@aws-sdk/client-dynamodb";
import { DynamoDBDocumentClient, PutCommand } from "@aws-sdk/lib-dynamodb";
import { randomUUID } from "crypto";
import { Logger } from "@aws-lambda-powertools/logger";
import { Metrics } from "@aws-lambda-powertools/metrics";
import { Tracer } from "@aws-lambda-powertools/tracer";
const logger = new Logger({ serviceName: "order-processor" });
const metrics = new Metrics({ namespace: "OrderProcessor" });
const tracer = new Tracer({ serviceName: "order-processor" });
const ddb = DynamoDBDocumentClient.from(new DynamoDBClient({}));
export const handler = async (
event: APIGatewayProxyEvent
): Promise => {
try {
const body = JSON.parse(event.body || "{}");
const orderId = randomUUID();
const order = {
orderId,
...body,
status: "PENDING",
createdAt: new Date().toISOString(),
};
await ddb.send(new PutCommand({
TableName: process.env.ORDER_TABLE,
Item: order,
}));
metrics.addMetric("OrderCreated", 1, "Count");
logger.info("Order created", { orderId });
return {
statusCode: 201,
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ orderId, status: "PENDING" }),
};
} catch (error) {
logger.error("Failed to create order", { error });
metrics.addMetric("OrderCreationError", 1, "Count");
return {
statusCode: 500,
body: JSON.stringify({ message: "Internal server error" }),
};
}
};
Local Development
The serverless-offline plugin provides a local Lambda emulator:
Start local API Gateway emulator
serverless offline --stage dev --httpPort 4000
Invoke a function directly
serverless invoke local --function createOrder \
--path test/fixtures/create-order.json
Run with warm container simulation
serverless offline --stage dev \
--noPrependStageInUrl \
--reloadHandler
Cold Start Optimization
Cold starts add latency when Lambda scales up a new execution environment:
Optimize for cold starts
provider:
Use AWS Graviton for better price/performance
architecture: arm64
Increase memory speeds up CPU allocation
(and proportionally reduces cold start time)
memorySize: 1024
functions:
latencyCritical:
handler: src/handlers/critical.handler
Provisioned concurrency for critical paths
provisionedConcurrency: 5
Reserve concurrency to prevent throttling
reservedConcurrency: 20
Code-level optimizations:
// Cold start optimization techniques
// 1. Lazy initialization outside handler (reused across invocations)
let client: DynamoDBDocumentClient;
function getClient(): D
Read the full article on AI Study Room for complete code examples, comparison tables, and related resources.
Found this useful? Check out more developer guides and tool comparisons on AI Study Room.
Top comments (0)