Skip to main content
RapidDev - Software Development Agency
cursor-tutorial

How to Make Cursor Follow Serverless Best Practices

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.

What you'll learn

  • How to create .cursorrules that enforce serverless patterns in Lambda code
  • How to prompt Cursor for cold-start-optimized handler functions
  • How to use @file context to reference serverless deployment configs
  • How to generate proper error handling and logging for Lambda
Book a free consultation
4.9Clutch rating
600+Happy partners
17+Countries served
190+Team members
Beginner8 min read10-15 minCursor Pro+, AWS Lambda, any runtimeMarch 2026RapidDev Engineering Team
TL;DR

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

1

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.

.cursor/rules/serverless.mdc
1---
2description: AWS Lambda serverless best practices
3globs: "**/handlers/**, **/lambdas/**, **/functions/**"
4alwaysApply: false
5---
6
7- Initialize SDK clients and DB connections OUTSIDE the handler (module scope)
8- Handler function must be stateless no global mutable state between invocations
9- Use environment variables for all configuration (process.env)
10- Keep handler functions under 50 lines extract logic to separate modules
11- Return proper API Gateway response format: { statusCode, headers, body }
12- Always include CORS headers in responses
13- 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 FORBIDDEN
16- Set timeout handling: check context.getRemainingTimeInMillis()
17- Wrap handler in try/catch with proper error response formatting

Pro 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.

2

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.

Cursor Composer prompt
1// Prompt to type in Cursor Composer (Cmd+I):
2// @serverless.yml @src/types/order.ts
3// Generate an API Gateway Lambda handler at src/handlers/createOrder.ts
4// Requirements:
5// - POST /orders endpoint
6// - Parse and validate request body using zod
7// - Insert order into DynamoDB (table from ORDER_TABLE env var)
8// - Return 201 with created order, 400 for validation errors, 500 for server errors
9// - Initialize DynamoDB client at module scope (outside handler)
10// - Include CORS headers
11// - Use structured JSON logging
12// - 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.

3

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.

Cursor Chat prompt
1// Prompt to type in Cursor Chat (Cmd+L):
2// @src/handlers/createOrder.ts
3// 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 packages
6// - lodash: import individual functions, not the whole library
7// - moment: replace with date-fns or native Date methods
8// 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.

4

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.

Cursor Composer prompt
1// Prompt to type in Cursor Composer (Cmd+I):
2// @src/handlers/createOrder.ts
3// Generate a Lambda middleware module at src/middleware/index.ts
4// Include middleware for:
5// 1. JSON body parsing with error handling
6// 2. CORS headers (configurable origins)
7// 3. Request validation using zod schemas
8// 4. Structured error response formatting
9// 5. Request ID logging from API Gateway context
10// 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.

5

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.

Cmd+K inline prompt
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), message
5// - requestId from the Lambda context
6// - Additional context as structured fields (not string concatenation)
7// - Error logs must include stack trace as a separate field
8// 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

src/handlers/createOrder.ts
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';
6
7// Initialize clients OUTSIDE handler (reused across warm invocations)
8const client = new DynamoDBClient({});
9const docClient = DynamoDBDocumentClient.from(client);
10const TABLE_NAME = process.env.ORDER_TABLE!;
11
12const 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});
20
21function log(level: string, message: string, data: Record<string, unknown> = {}) {
22 console.log(JSON.stringify({ timestamp: new Date().toISOString(), level, message, ...data }));
23}
24
25export async function handler(
26 event: APIGatewayProxyEvent,
27 context: Context
28): Promise<APIGatewayProxyResult> {
29 const headers = {
30 'Content-Type': 'application/json',
31 'Access-Control-Allow-Origin': '*',
32 'Access-Control-Allow-Methods': 'POST, OPTIONS',
33 };
34
35 try {
36 const body = JSON.parse(event.body || '{}');
37 const parsed = OrderSchema.safeParse(body);
38
39 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 }
43
44 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 };
51
52 await docClient.send(new PutCommand({ TableName: TABLE_NAME, Item: order }));
53 log('INFO', 'Order created', { orderId: order.id, requestId: context.awsRequestId });
54
55 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.

ChatGPT Prompt

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.

Cursor Prompt

@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.

RapidDev

Talk to an Expert

Our team has built 600+ apps. Get personalized help with your project.

Book a free consultation

Need help with your project?

Our experts have built 600+ apps and can accelerate your development. Book a free consultation — no strings attached.

Book a free consultation

We put the rapid in RapidDev

Need a dedicated strategic tech and growth partner? Discover what RapidDev can do for your business! Book a call with our team to schedule a free, no-obligation consultation. We'll discuss your project and provide a custom quote at no cost.