Claude-skill-registry aws-lambda

Build serverless applications with AWS Lambda and TypeScript. Covers handler patterns, API Gateway integration, DynamoDB operations, SQS/S3/DynamoDB Streams event sources, SAM templates, and CDK infrastructure. Use for serverless APIs, event-driven architectures, and AWS backend development.

install
source · Clone the upstream repo
git clone https://github.com/majiayu000/claude-skill-registry
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/aws-lambda" ~/.claude/skills/majiayu000-claude-skill-registry-aws-lambda && rm -rf "$T"
manifest: skills/data/aws-lambda/SKILL.md
source content

AWS Lambda Skill

Expert guidance for AWS Lambda with TypeScript, API Gateway, DynamoDB, and serverless patterns.

Triggers

Use this skill when you see:

  • aws lambda, lambda function, serverless aws
  • api gateway, sam template, cdk lambda
  • dynamodb streams, sqs handler, s3 trigger
  • lambda handler, cold start, provisioned concurrency

Instructions

Lambda Handler Patterns

Basic Handler with TypeScript

import type {
  APIGatewayProxyEvent,
  APIGatewayProxyResult,
  Context
} from 'aws-lambda';

export const handler = async (
  event: APIGatewayProxyEvent,
  context: Context
): Promise<APIGatewayProxyResult> => {
  try {
    const body = JSON.parse(event.body ?? '{}');

    // Your logic here
    const result = await processRequest(body);

    return {
      statusCode: 200,
      headers: {
        'Content-Type': 'application/json',
        'Access-Control-Allow-Origin': '*',
      },
      body: JSON.stringify(result),
    };
  } catch (error) {
    console.error('Error:', error);

    return {
      statusCode: 500,
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ error: 'Internal Server Error' }),
    };
  }
};

Middleware Pattern with Middy

import middy from '@middy/core';
import jsonBodyParser from '@middy/http-json-body-parser';
import httpErrorHandler from '@middy/http-error-handler';
import cors from '@middy/http-cors';
import validator from '@middy/validator';
import { transpileSchema } from '@middy/validator/transpile';

const inputSchema = {
  type: 'object',
  properties: {
    body: {
      type: 'object',
      properties: {
        email: { type: 'string', format: 'email' },
        name: { type: 'string', minLength: 1 },
      },
      required: ['email', 'name'],
    },
  },
};

const baseHandler = async (event: APIGatewayProxyEvent) => {
  const { email, name } = event.body as { email: string; name: string };

  const user = await createUser({ email, name });

  return {
    statusCode: 201,
    body: JSON.stringify(user),
  };
};

export const handler = middy(baseHandler)
  .use(jsonBodyParser())
  .use(validator({ eventSchema: transpileSchema(inputSchema) }))
  .use(httpErrorHandler())
  .use(cors());

Event Source Handlers

SQS Handler

import type { SQSEvent, SQSHandler } from 'aws-lambda';

export const sqsHandler: SQSHandler = async (event: SQSEvent) => {
  const results = await Promise.allSettled(
    event.Records.map(async (record) => {
      const message = JSON.parse(record.body);
      await processMessage(message);
    })
  );

  // Return batch item failures for partial batch response
  const failures = results
    .map((result, index) =>
      result.status === 'rejected'
        ? { itemIdentifier: event.Records[index].messageId }
        : null
    )
    .filter(Boolean);

  return { batchItemFailures: failures };
};

DynamoDB Streams Handler

import type { DynamoDBStreamEvent, DynamoDBStreamHandler } from 'aws-lambda';
import { unmarshall } from '@aws-sdk/util-dynamodb';

export const streamHandler: DynamoDBStreamHandler = async (
  event: DynamoDBStreamEvent
) => {
  for (const record of event.Records) {
    if (record.eventName === 'INSERT' && record.dynamodb?.NewImage) {
      const item = unmarshall(record.dynamodb.NewImage);
      await handleNewItem(item);
    }
  }
};

S3 Handler

import type { S3Event, S3Handler } from 'aws-lambda';

export const s3Handler: S3Handler = async (event: S3Event) => {
  for (const record of event.Records) {
    const bucket = record.s3.bucket.name;
    const key = decodeURIComponent(record.s3.object.key.replace(/\+/g, ' '));

    await processS3Object(bucket, key);
  }
};

DynamoDB Integration

import { DynamoDBClient } from '@aws-sdk/client-dynamodb';
import {
  DynamoDBDocumentClient,
  GetCommand,
  PutCommand,
  QueryCommand,
  UpdateCommand,
} from '@aws-sdk/lib-dynamodb';

const client = new DynamoDBClient({});
const docClient = DynamoDBDocumentClient.from(client);

const TABLE_NAME = process.env.TABLE_NAME!;

// Get item
async function getUser(userId: string) {
  const result = await docClient.send(new GetCommand({
    TableName: TABLE_NAME,
    Key: { PK: `USER#${userId}`, SK: `USER#${userId}` },
  }));

  return result.Item;
}

// Put item
async function createUser(user: User) {
  await docClient.send(new PutCommand({
    TableName: TABLE_NAME,
    Item: {
      PK: `USER#${user.id}`,
      SK: `USER#${user.id}`,
      ...user,
      createdAt: new Date().toISOString(),
    },
    ConditionExpression: 'attribute_not_exists(PK)',
  }));
}

// Query with GSI
async function getUserOrders(userId: string) {
  const result = await docClient.send(new QueryCommand({
    TableName: TABLE_NAME,
    IndexName: 'GSI1',
    KeyConditionExpression: 'GSI1PK = :pk AND begins_with(GSI1SK, :sk)',
    ExpressionAttributeValues: {
      ':pk': `USER#${userId}`,
      ':sk': 'ORDER#',
    },
  }));

  return result.Items ?? [];
}

SAM Template

# template.yaml
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31

Globals:
  Function:
    Timeout: 30
    Runtime: nodejs20.x
    MemorySize: 256
    Architectures:
      - arm64
    Environment:
      Variables:
        TABLE_NAME: !Ref UsersTable

Resources:
  ApiFunction:
    Type: AWS::Serverless::Function
    Properties:
      Handler: dist/handlers/api.handler
      Events:
        GetUsers:
          Type: Api
          Properties:
            Path: /users
            Method: GET
        CreateUser:
          Type: Api
          Properties:
            Path: /users
            Method: POST
      Policies:
        - DynamoDBCrudPolicy:
            TableName: !Ref UsersTable
    Metadata:
      BuildMethod: esbuild
      BuildProperties:
        Minify: true
        Target: es2022

  UsersTable:
    Type: AWS::DynamoDB::Table
    Properties:
      BillingMode: PAY_PER_REQUEST
      AttributeDefinitions:
        - AttributeName: PK
          AttributeType: S
        - AttributeName: SK
          AttributeType: S
      KeySchema:
        - AttributeName: PK
          KeyType: HASH
        - AttributeName: SK
          KeyType: RANGE

Outputs:
  ApiUrl:
    Value: !Sub 'https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod'

CDK Lambda

import * as cdk from 'aws-cdk-lib';
import * as lambda from 'aws-cdk-lib/aws-lambda';
import * as apigateway from 'aws-cdk-lib/aws-apigateway';
import * as dynamodb from 'aws-cdk-lib/aws-dynamodb';
import { NodejsFunction } from 'aws-cdk-lib/aws-lambda-nodejs';

export class ApiStack extends cdk.Stack {
  constructor(scope: cdk.App, id: string) {
    super(scope, id);

    const table = new dynamodb.Table(this, 'UsersTable', {
      partitionKey: { name: 'PK', type: dynamodb.AttributeType.STRING },
      sortKey: { name: 'SK', type: dynamodb.AttributeType.STRING },
      billingMode: dynamodb.BillingMode.PAY_PER_REQUEST,
    });

    const fn = new NodejsFunction(this, 'ApiHandler', {
      entry: 'src/handlers/api.ts',
      runtime: lambda.Runtime.NODEJS_20_X,
      architecture: lambda.Architecture.ARM_64,
      environment: {
        TABLE_NAME: table.tableName,
      },
      bundling: {
        minify: true,
      },
    });

    table.grantReadWriteData(fn);

    new apigateway.LambdaRestApi(this, 'Api', {
      handler: fn,
    });
  }
}

Best Practices

PracticeImplementation
Cold startsUse ARM64, minimize dependencies, use provisioned concurrency for critical paths
ConnectionsReuse SDK clients outside handler, use connection pooling
Error handlingUse structured error responses, implement retries with backoff
LoggingUse structured JSON logging, include request IDs
SecurityUse IAM roles, validate input, encrypt sensitive data

Common Workflows

New Lambda Function

  1. Create handler file with TypeScript types
  2. Add middleware for validation/error handling
  3. Configure SAM/CDK template
  4. Set up IAM permissions
  5. Deploy and test

Event-Driven Architecture

  1. Define event source (SQS, S3, DynamoDB Streams)
  2. Create event handler with proper typing
  3. Implement batch failure handling
  4. Configure DLQ for failed events
  5. Monitor with CloudWatch