home / skills / bbeierle12 / skill-mcp-claude / aws-skills

aws-skills skill

/skills/aws-skills

This skill helps you design and deploy scalable AWS infrastructure using CDK patterns, serverless architectures, and cost-aware security with best practices.

npx playbooks add skill bbeierle12/skill-mcp-claude --skill aws-skills

Review the files below or copy the command above to add this skill to your agents.

Files (2)
SKILL.md
5.1 KB
---
name: aws-skills
description: AWS development with CDK best practices, serverless patterns, cost optimization, and event-driven architecture. Use when deploying to AWS, writing Lambda functions, configuring API Gateway, working with DynamoDB, S3, or any AWS service.
---

# AWS Development Skills

## AWS CDK Best Practices

### Project Structure
```
infrastructure/
├── bin/
│   └── app.ts              # CDK app entry point
├── lib/
│   ├── stacks/
│   │   ├── api-stack.ts
│   │   ├── database-stack.ts
│   │   └── storage-stack.ts
│   ├── constructs/
│   │   ├── lambda-function.ts
│   │   └── api-gateway.ts
│   └── config/
│       └── environment.ts
├── lambda/
│   └── handlers/
├── cdk.json
└── package.json
```

### Stack Definition
```typescript
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 { Construct } from 'constructs';

interface ApiStackProps extends cdk.StackProps {
  environment: string;
  domainName?: string;
}

export class ApiStack extends cdk.Stack {
  public readonly api: apigateway.RestApi;

  constructor(scope: Construct, id: string, props: ApiStackProps) {
    super(scope, id, props);

    const handler = new lambda.Function(this, 'ApiHandler', {
      runtime: lambda.Runtime.NODEJS_18_X,
      handler: 'index.handler',
      code: lambda.Code.fromAsset('lambda/handlers'),
      environment: { NODE_ENV: props.environment },
      memorySize: 256,
      timeout: cdk.Duration.seconds(30),
    });

    this.api = new apigateway.RestApi(this, 'Api', {
      restApiName: `${props.environment}-api`,
      deployOptions: {
        stageName: props.environment,
        throttlingRateLimit: 1000,
        throttlingBurstLimit: 500,
      },
    });

    const integration = new apigateway.LambdaIntegration(handler);
    this.api.root.addMethod('GET', integration);
  }
}
```

## Serverless Patterns

### Lambda Best Practices
```typescript
import { APIGatewayProxyHandler } from 'aws-lambda';

// Initialize outside handler for connection reuse
const dynamodb = new DynamoDB.DocumentClient();

export const handler: APIGatewayProxyHandler = async (event) => {
  try {
    const body = JSON.parse(event.body || '{}');
    const result = await processRequest(body);
    return response(200, result);
  } catch (error) {
    console.error('Error:', error);
    return response(500, { error: 'Internal server error' });
  }
};

function response(statusCode: number, body: any) {
  return {
    statusCode,
    headers: {
      'Content-Type': 'application/json',
      'Access-Control-Allow-Origin': '*',
    },
    body: JSON.stringify(body),
  };
}
```

### DynamoDB Single-Table Design
```typescript
const table = new dynamodb.Table(this, 'Table', {
  partitionKey: { name: 'PK', type: dynamodb.AttributeType.STRING },
  sortKey: { name: 'SK', type: dynamodb.AttributeType.STRING },
  billingMode: dynamodb.BillingMode.PAY_PER_REQUEST,
});

// Access patterns
// User by ID:      PK=USER#123, SK=PROFILE
// User's orders:   PK=USER#123, SK=ORDER#timestamp
// Order by ID:     GSI1PK=ORDER#456, GSI1SK=ORDER#456
```

### Event-Driven with EventBridge
```typescript
const rule = new events.Rule(this, 'OrderCreatedRule', {
  eventPattern: {
    source: ['orders'],
    detailType: ['OrderCreated'],
  },
});

rule.addTarget(new targets.LambdaFunction(processOrderHandler));
rule.addTarget(new targets.SqsQueue(notificationQueue));
```

### SQS + Lambda Pattern
```typescript
const dlq = new sqs.Queue(this, 'DeadLetterQueue');

const queue = new sqs.Queue(this, 'ProcessingQueue', {
  visibilityTimeout: cdk.Duration.seconds(300),
  deadLetterQueue: { queue: dlq, maxReceiveCount: 3 },
});

processor.addEventSource(new SqsEventSource(queue, {
  batchSize: 10,
  maxBatchingWindow: cdk.Duration.seconds(5),
}));
```

## Cost Optimization

### Lambda Optimization
```typescript
const handler = new lambda.Function(this, 'Handler', {
  memorySize: 256,
  timeout: cdk.Duration.seconds(10),
  architecture: lambda.Architecture.ARM_64, // Cost savings
});
```

### S3 Lifecycle Rules
```typescript
const bucket = new s3.Bucket(this, 'Bucket', {
  lifecycleRules: [{
    transitions: [
      { storageClass: s3.StorageClass.INFREQUENT_ACCESS, transitionAfter: cdk.Duration.days(30) },
      { storageClass: s3.StorageClass.GLACIER, transitionAfter: cdk.Duration.days(90) },
    ],
    expiration: cdk.Duration.days(365),
  }],
});
```

## Security Best Practices

### IAM Least Privilege
```typescript
// Grant methods (preferred)
table.grantReadWriteData(handler);
bucket.grantRead(handler);
```

### Secrets Management
```typescript
const secret = secretsmanager.Secret.fromSecretNameV2(this, 'DbSecret', 'prod/db/credentials');
secret.grantRead(handler);
```

## Monitoring

### CloudWatch Alarms
```typescript
new cloudwatch.Alarm(this, 'LambdaErrors', {
  metric: handler.metricErrors(),
  threshold: 1,
  evaluationPeriods: 1,
});
```

### X-Ray Tracing
```typescript
const handler = new lambda.Function(this, 'Handler', {
  tracing: lambda.Tracing.ACTIVE,
});
```

Overview

This skill captures practical AWS development patterns using the CDK, serverless best practices, cost optimization tactics, and event-driven architecture patterns. It focuses on deployable project structure, Lambda and DynamoDB patterns, SQS/EventBridge integrations, security, and monitoring recommendations. Use it as a concise guide when building and operating AWS workloads with JavaScript/TypeScript CDK stacks.

How this skill works

The skill describes a recommended CDK project layout and provides sample stack and construct patterns for API Gateway, Lambda, DynamoDB, S3, SQS, and EventBridge. It explains runtime considerations such as Lambda initialization for connection reuse, single-table DynamoDB access patterns, dead-letter queue handling, lifecycle transitions for S3, and tracing/alarms for observability. It also highlights IAM least-privilege and secrets access patterns to reduce blast radius.

When to use it

  • When scaffolding a new CDK project and defining stack/construct boundaries
  • When writing Lambda functions that need optimized cold-starts and connection reuse
  • When designing DynamoDB single-table access patterns and GSIs
  • When implementing asynchronous workflows with SQS or event-driven flows with EventBridge
  • When reducing costs with Lambda sizing, Graviton/ARM architecture, and S3 lifecycle rules

Best practices

  • Structure code into stacks, reusable constructs, and separate lambda handlers for clarity and reuse
  • Initialize clients (DB, caches) outside handlers to enable connection reuse across invocations
  • Apply least-privilege IAM grants via resource grant helpers instead of broad policies
  • Use dead-letter queues, visibility timeouts, and batching for resilient SQS+Lambda processing
  • Prefer pay-per-request for unpredictable traffic, tune Lambda memory/timeout, and use ARM64 for cost savings
  • Enable tracing and alarms (X-Ray, CloudWatch) for errors and latency to detect regressions early

Example use cases

  • Deploying a REST API backed by Lambda and DynamoDB with CDK-managed stages and throttling
  • Implementing a single-table design to store users, orders, and other entities with GSIs for alternate lookups
  • Processing order events via EventBridge, invoking multiple targets like Lambda and SQS for fan-out
  • Batch-processing messages from SQS with retries and a dead-letter queue for poison message handling
  • Applying S3 lifecycle rules to move objects to infrequent access and Glacier to lower storage costs

FAQ

How do I reduce Lambda costs while keeping performance?

Right-size memory and timeout, prefer ARM64 architecture when compatible, and keep cold-starts low by minimizing bundle size and reusing connections initialized outside the handler.

When should I choose single-table DynamoDB?

Choose it when you can define clear access patterns that share a partition key structure; it reduces joins and scales well but requires careful key design and GSIs for alternate queries.