# How to Make Cursor Follow Serverless Best Practices

- Tool: Cursor
- Difficulty: Beginner
- Time required: 10-15 min
- Compatibility: Cursor Pro+, AWS Lambda, any runtime
- Last updated: March 2026

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

## Before you start

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

```
---
description: AWS Lambda serverless best practices
globs: "**/handlers/**, **/lambdas/**, **/functions/**"
alwaysApply: false
---

- Initialize SDK clients and DB connections OUTSIDE the handler (module scope)
- Handler function must be stateless — no global mutable state between invocations
- Use environment variables for all configuration (process.env)
- Keep handler functions under 50 lines — extract logic to separate modules
- Return proper API Gateway response format: { statusCode, headers, body }
- Always include CORS headers in responses
- Use structured JSON logging (not console.log with strings)
- Import only what you need: import { S3Client } from '@aws-sdk/client-s3'
- Never import entire AWS SDK v2: import AWS from 'aws-sdk' is FORBIDDEN
- Set timeout handling: check context.getRemainingTimeInMillis()
- 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.

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

```
// Prompt to type in Cursor Chat (Cmd+L):
// @src/handlers/createOrder.ts
// Review the imports in this Lambda handler for bundle size.
// Replace any broad imports with targeted imports:
// - AWS SDK v3: import specific clients, not entire packages
// - lodash: import individual functions, not the whole library
// - moment: replace with date-fns or native Date methods
// 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.

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

```
// Select a handler file, press Cmd+K:
// Replace all console.log calls with structured JSON logging.
// Each log entry must include:
// - timestamp, level (INFO/WARN/ERROR), message
// - requestId from the Lambda context
// - Additional context as structured fields (not string concatenation)
// - Error logs must include stack trace as a separate field
// 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 code example

File: `src/handlers/createOrder.ts`

```typescript
import { DynamoDBClient } from '@aws-sdk/client-dynamodb';
import { DynamoDBDocumentClient, PutCommand } from '@aws-sdk/lib-dynamodb';
import { randomUUID } from 'crypto';
import { z } from 'zod';
import type { APIGatewayProxyEvent, APIGatewayProxyResult, Context } from 'aws-lambda';

// Initialize clients OUTSIDE handler (reused across warm invocations)
const client = new DynamoDBClient({});
const docClient = DynamoDBDocumentClient.from(client);
const TABLE_NAME = process.env.ORDER_TABLE!;

const OrderSchema = z.object({
  userId: z.string().uuid(),
  items: z.array(z.object({
    productId: z.string(),
    quantity: z.number().int().positive(),
    priceCents: z.number().int().positive(),
  })).min(1),
});

function log(level: string, message: string, data: Record<string, unknown> = {}) {
  console.log(JSON.stringify({ timestamp: new Date().toISOString(), level, message, ...data }));
}

export async function handler(
  event: APIGatewayProxyEvent,
  context: Context
): Promise<APIGatewayProxyResult> {
  const headers = {
    'Content-Type': 'application/json',
    'Access-Control-Allow-Origin': '*',
    'Access-Control-Allow-Methods': 'POST, OPTIONS',
  };

  try {
    const body = JSON.parse(event.body || '{}');
    const parsed = OrderSchema.safeParse(body);

    if (!parsed.success) {
      log('WARN', 'Validation failed', { errors: parsed.error.issues, requestId: context.awsRequestId });
      return { statusCode: 400, headers, body: JSON.stringify({ errors: parsed.error.issues }) };
    }

    const order = {
      id: randomUUID(),
      ...parsed.data,
      totalCents: parsed.data.items.reduce((sum, i) => sum + i.priceCents * i.quantity, 0),
      status: 'pending',
      createdAt: new Date().toISOString(),
    };

    await docClient.send(new PutCommand({ TableName: TABLE_NAME, Item: order }));
    log('INFO', 'Order created', { orderId: order.id, requestId: context.awsRequestId });

    return { statusCode: 201, headers, body: JSON.stringify(order) };
  } catch (error) {
    log('ERROR', 'Failed to create order', {
      error: (error as Error).message,
      stack: (error as Error).stack,
      requestId: context.awsRequestId,
    });
    return { statusCode: 500, headers, body: JSON.stringify({ error: 'Internal server error' }) };
  }
}
```

## Common mistakes

- **Initializing SDK clients inside the handler function** — Creating new SDK clients on every invocation wastes cold start time. Clients initialized at module scope are reused across warm invocations. Fix: Add a rule requiring module-scope initialization and reference it when generating handlers.
- **Using AWS SDK v2 (import AWS from 'aws-sdk')** — AWS SDK v2 is 70MB+ bundled, dramatically increasing cold start time. SDK v3 allows importing only the clients you need. Fix: Add a .cursorrules entry: 'NEVER import aws-sdk v2. Use @aws-sdk/client-* v3 packages with targeted imports.'
- **Not handling Lambda timeout gracefully** — If a Lambda times out mid-execution, the client receives a generic 502 error with no useful information for debugging. Fix: 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

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

---

Source: https://www.rapidevelopers.com/cursor-tutorial/how-to-prompt-cursor-ai-to-use-serverless-best-practices-in-aws-lambda-code
© RapidDev — https://www.rapidevelopers.com/cursor-tutorial/how-to-prompt-cursor-ai-to-use-serverless-best-practices-in-aws-lambda-code
