Make Cursor follow serverless best practices by adding AWS Lambda-specific rules to .cursorrules that enforce cold start optimization, connection reuse, proper error handling, and minimal bundle size. Reference your serverless.yml or SAM template with @file so Cursor generates functions that match your actual deployment configuration.
Why Cursor Needs Serverless-Specific Rules
Cursor defaults to server-based patterns when generating backend code: it creates long-running connections, imports heavy libraries, and assumes persistent state. Serverless functions require the opposite approach: minimal cold start time, connection pooling outside the handler, lightweight dependencies, and stateless execution. This tutorial shows you how to configure Cursor to generate Lambda-friendly code with proper initialization patterns, error handling, and observability.
Prerequisites
- Cursor installed (Pro recommended)
- An AWS Lambda project (Serverless Framework, SAM, or CDK)
- Basic understanding of AWS Lambda execution model
- Node.js or Python runtime configured
Step-by-step guide
Create serverless rules for Cursor
Create serverless rules for Cursor
Add a .cursor/rules/serverless.mdc file that enforces Lambda best practices. These rules teach Cursor about the serverless execution model: cold starts, handler isolation, connection reuse, and bundle size limits. The rules auto-attach when editing Lambda handler files.
1---2description: AWS Lambda serverless best practices3globs: "**/handlers/**, **/lambdas/**, **/functions/**"4alwaysApply: false5---67- Initialize SDK clients and DB connections OUTSIDE the handler (module scope)8- Handler function must be stateless — no global mutable state between invocations9- Use environment variables for all configuration (process.env)10- Keep handler functions under 50 lines — extract logic to separate modules11- Return proper API Gateway response format: { statusCode, headers, body }12- Always include CORS headers in responses13- Use structured JSON logging (not console.log with strings)14- Import only what you need: import { S3Client } from '@aws-sdk/client-s3'15- Never import entire AWS SDK v2: import AWS from 'aws-sdk' is FORBIDDEN16- Set timeout handling: check context.getRemainingTimeInMillis()17- Wrap handler in try/catch with proper error response formattingPro tip: If you use Python Lambdas, change the imports rule to: 'Use boto3 client creation at module level, not inside the handler function.'
Expected result: Serverless rules auto-attach when editing any Lambda handler file.
Generate a Lambda handler with Composer
Generate a Lambda handler with Composer
Open Composer with Cmd+I and reference your serverless configuration file. Describe the Lambda function's purpose and Cursor will generate a handler that follows all your serverless rules. Always specify the trigger type (API Gateway, SQS, EventBridge) so Cursor generates the correct event parsing.
1// Prompt to type in Cursor Composer (Cmd+I):2// @serverless.yml @src/types/order.ts3// Generate an API Gateway Lambda handler at src/handlers/createOrder.ts4// Requirements:5// - POST /orders endpoint6// - Parse and validate request body using zod7// - Insert order into DynamoDB (table from ORDER_TABLE env var)8// - Return 201 with created order, 400 for validation errors, 500 for server errors9// - Initialize DynamoDB client at module scope (outside handler)10// - Include CORS headers11// - Use structured JSON logging12// - Add timeout awareness with context.getRemainingTimeInMillis()Pro tip: Mention the specific trigger type in your prompt. An API Gateway Lambda needs different event parsing than an SQS or EventBridge Lambda.
Expected result: A Lambda handler with proper initialization patterns, error handling, and API Gateway response formatting.
Optimize imports for bundle size
Optimize imports for bundle size
Use Chat (Cmd+L) to review and optimize imports in your Lambda handlers. Large imports increase cold start time. Reference the handler file and ask Cursor to replace broad imports with targeted ones. This is especially important for the AWS SDK.
1// Prompt to type in Cursor Chat (Cmd+L):2// @src/handlers/createOrder.ts3// Review the imports in this Lambda handler for bundle size.4// Replace any broad imports with targeted imports:5// - AWS SDK v3: import specific clients, not entire packages6// - lodash: import individual functions, not the whole library7// - moment: replace with date-fns or native Date methods8// List each change and explain the bundle size impact.Expected result: Cursor identifies over-broad imports and provides optimized alternatives that reduce cold start time.
Generate middleware for common Lambda patterns
Generate middleware for common Lambda patterns
Use Composer to generate reusable middleware that handles cross-cutting concerns like authentication, validation, CORS, and error formatting. This follows the middy middleware pattern for AWS Lambda and keeps individual handlers clean and focused.
1// Prompt to type in Cursor Composer (Cmd+I):2// @src/handlers/createOrder.ts3// Generate a Lambda middleware module at src/middleware/index.ts4// Include middleware for:5// 1. JSON body parsing with error handling6// 2. CORS headers (configurable origins)7// 3. Request validation using zod schemas8// 4. Structured error response formatting9// 5. Request ID logging from API Gateway context10// Use the middy pattern or a simple wrapper function.11// Show how to apply middleware to the createOrder handler.Pro tip: If you use middy (@middy/core), reference it in your rules and Cursor will generate proper middy middleware plugins instead of custom wrappers.
Expected result: A reusable middleware module and an updated handler demonstrating middleware usage.
Generate CloudWatch-compatible structured logging
Generate CloudWatch-compatible structured logging
Replace console.log calls with structured JSON logging that works well with CloudWatch Logs Insights. Use Cmd+K to refactor existing handlers or generate a logging utility that all handlers can import.
1// Select a handler file, press Cmd+K:2// Replace all console.log calls with structured JSON logging.3// Each log entry must include:4// - timestamp, level (INFO/WARN/ERROR), message5// - requestId from the Lambda context6// - Additional context as structured fields (not string concatenation)7// - Error logs must include stack trace as a separate field8// Format: JSON.stringify({ timestamp, level, message, requestId, ...data })Pro tip: Structured JSON logs enable CloudWatch Logs Insights queries like: fields @timestamp, message | filter level = 'ERROR' | sort @timestamp desc
Expected result: All console.log calls are replaced with structured JSON logging including request context.
Complete working example
1import { DynamoDBClient } from '@aws-sdk/client-dynamodb';2import { DynamoDBDocumentClient, PutCommand } from '@aws-sdk/lib-dynamodb';3import { randomUUID } from 'crypto';4import { z } from 'zod';5import type { APIGatewayProxyEvent, APIGatewayProxyResult, Context } from 'aws-lambda';67// Initialize clients OUTSIDE handler (reused across warm invocations)8const client = new DynamoDBClient({});9const docClient = DynamoDBDocumentClient.from(client);10const TABLE_NAME = process.env.ORDER_TABLE!;1112const OrderSchema = z.object({13 userId: z.string().uuid(),14 items: z.array(z.object({15 productId: z.string(),16 quantity: z.number().int().positive(),17 priceCents: z.number().int().positive(),18 })).min(1),19});2021function log(level: string, message: string, data: Record<string, unknown> = {}) {22 console.log(JSON.stringify({ timestamp: new Date().toISOString(), level, message, ...data }));23}2425export async function handler(26 event: APIGatewayProxyEvent,27 context: Context28): Promise<APIGatewayProxyResult> {29 const headers = {30 'Content-Type': 'application/json',31 'Access-Control-Allow-Origin': '*',32 'Access-Control-Allow-Methods': 'POST, OPTIONS',33 };3435 try {36 const body = JSON.parse(event.body || '{}');37 const parsed = OrderSchema.safeParse(body);3839 if (!parsed.success) {40 log('WARN', 'Validation failed', { errors: parsed.error.issues, requestId: context.awsRequestId });41 return { statusCode: 400, headers, body: JSON.stringify({ errors: parsed.error.issues }) };42 }4344 const order = {45 id: randomUUID(),46 ...parsed.data,47 totalCents: parsed.data.items.reduce((sum, i) => sum + i.priceCents * i.quantity, 0),48 status: 'pending',49 createdAt: new Date().toISOString(),50 };5152 await docClient.send(new PutCommand({ TableName: TABLE_NAME, Item: order }));53 log('INFO', 'Order created', { orderId: order.id, requestId: context.awsRequestId });5455 return { statusCode: 201, headers, body: JSON.stringify(order) };56 } catch (error) {57 log('ERROR', 'Failed to create order', {58 error: (error as Error).message,59 stack: (error as Error).stack,60 requestId: context.awsRequestId,61 });62 return { statusCode: 500, headers, body: JSON.stringify({ error: 'Internal server error' }) };63 }64}Common mistakes when making Cursor Follow Serverless Best Practices
Why it's a problem: Initializing SDK clients inside the handler function
How to avoid: Add a rule requiring module-scope initialization and reference it when generating handlers.
Why it's a problem: Using AWS SDK v2 (import AWS from 'aws-sdk')
How to avoid: Add a .cursorrules entry: 'NEVER import aws-sdk v2. Use @aws-sdk/client-* v3 packages with targeted imports.'
Why it's a problem: Not handling Lambda timeout gracefully
How to avoid: Check context.getRemainingTimeInMillis() before long operations and return a clear timeout response if time is running low.
Best practices
- Initialize SDK clients and database connections at module scope, outside the handler function
- Use AWS SDK v3 with targeted imports to minimize bundle size and cold start time
- Wrap every handler in try/catch with structured error response formatting
- Use structured JSON logging for CloudWatch Logs Insights compatibility
- Keep handler functions under 50 lines by extracting business logic into separate modules
- Always return proper API Gateway response format with CORS headers
- Set up .cursor/rules/ with auto-attaching globs for handler directories
Still stuck?
Copy one of these prompts to get a personalized, step-by-step explanation.
Generate an AWS Lambda handler in TypeScript for a POST /orders endpoint. Use DynamoDB for storage, zod for validation, structured JSON logging, and proper API Gateway response formatting. Initialize all SDK clients outside the handler for cold start optimization.
@serverless.yml @src/types/order.ts Generate an API Gateway Lambda handler at src/handlers/createOrder.ts. Initialize DynamoDB client at module scope. Parse body with zod, insert to DynamoDB (table from ORDER_TABLE env var), return 201/400/500 with CORS headers. Use AWS SDK v3 targeted imports and structured JSON logging.
Frequently asked questions
What are the best practices for using Cursor AI with serverless?
Create .cursorrules that enforce module-scope initialization, AWS SDK v3 targeted imports, structured logging, and proper error handling. Reference your serverless.yml with @file in prompts so Cursor knows your function configuration. Generate middleware for cross-cutting concerns.
Can Cursor generate Serverless Framework or SAM templates?
Yes. Prompt Cursor with your handler file references and ask for serverless.yml or template.yaml generation. Cursor will create function definitions, API Gateway events, DynamoDB table resources, and IAM role permissions.
How do I make Cursor generate small Lambda bundles?
Add a rule requiring targeted imports and forbidding large utility libraries. List specific forbidden patterns: no lodash (use lodash-es or native), no moment (use date-fns), no AWS SDK v2. Cursor will use lightweight alternatives.
Does Cursor understand Lambda cold starts?
Cursor understands the concept but does not optimize for cold starts by default. You must add explicit rules about module-scope initialization, minimal imports, and lightweight dependencies. Once these rules exist, Cursor follows them consistently.
Can Cursor help debug Lambda errors from CloudWatch?
Yes. Paste CloudWatch log entries into Chat (Cmd+L) with the handler file referenced. Cursor will parse the log output, identify the error, and suggest fixes. Structured JSON logging makes this process more reliable.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation