Skip to content

Commit 2c76e14

Browse files
authored
feat: add comprehensive LLM context guide for CDK Serverless (#345)
- Introduced a new `llm.md` document that serves as a detailed guide for AI assistants using the CDK Serverless library. - The guide includes an overview of the library, core concepts, key components, common workflows, best practices, and common pitfalls to enhance user understanding and facilitate serverless application development on AWS.
1 parent 2f88627 commit 2c76e14

File tree

1 file changed

+392
-0
lines changed

1 file changed

+392
-0
lines changed

llm.md

Lines changed: 392 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,392 @@
1+
# CDK Serverless - LLM Context Guide
2+
3+
This document serves as a comprehensive guide for AI assistants working with the CDK Serverless library. It provides an overview of the library's purpose, architecture, key components, and usage patterns to help generate accurate and contextually appropriate code.
4+
5+
## Library Overview
6+
7+
CDK Serverless is a toolkit for building serverless applications on AWS using the AWS Cloud Development Kit (CDK). It provides higher-level (L3) constructs, utility libraries, and project management features designed to simplify serverless development.
8+
9+
### Core Concepts
10+
11+
1. **Higher-level CDK Constructs**: Pre-configured L3 constructs for common serverless patterns like REST APIs, GraphQL APIs, DynamoDB tables, and authentication.
12+
13+
2. **Lambda Function Utilities**: Helper functions and classes for creating Lambda handlers with standardized error handling, authentication, and typing.
14+
15+
3. **Projen Integration**: Project setup tools for quickly scaffolding serverless applications with best practices.
16+
17+
4. **Testing Utilities**: Tools for unit testing Lambda functions and integration testing deployed applications.
18+
19+
## Key Components
20+
21+
### CDK Constructs (`src/constructs/`)
22+
23+
#### `AssetCdn`
24+
25+
Creates an S3 bucket for asset storage with CloudFront distribution for secure HTTPS asset serving.
26+
27+
```typescript
28+
const cdn = new AssetCdn(this, 'MyCdn', {
29+
domainName: 'example.com',
30+
hostName: 'cdn',
31+
cors: true,
32+
});
33+
```
34+
35+
#### `CognitoAuthentication`
36+
37+
Sets up Cognito User Pools and optional Identity Pools for authentication.
38+
39+
```typescript
40+
const auth = new CognitoAuthentication(this, 'Auth', {
41+
userPoolName: 'my-users',
42+
selfSignUp: true,
43+
emailVerification: true,
44+
userGroups: ['admin', 'user'],
45+
identityPool: true,
46+
});
47+
```
48+
49+
#### `GraphQlApi`
50+
51+
Creates an AWS AppSync GraphQL API with resolvers and authentication.
52+
53+
```typescript
54+
const api = new GraphQlApi(this, 'Api', {
55+
apiName: 'MyGraphQLApi',
56+
stageName: 'dev',
57+
definitionFileName: 'schema.graphql',
58+
authentication: auth,
59+
datastore: table,
60+
domainName: 'example.com',
61+
apiHostname: 'api',
62+
});
63+
64+
// Add resolvers
65+
api.addLambdaResolver('Query', 'getItems', handler);
66+
api.addVtlResolver('Mutation', 'createItem', {
67+
operation: 'PutItem',
68+
key: util.dynamodb.toMapValues({ PK: 'ITEM#${context.arguments.id}' }),
69+
attributeValues: util.dynamodb.toMapValues({ ...context.arguments }),
70+
});
71+
```
72+
73+
#### `RestApi`
74+
75+
Creates an AWS API Gateway REST API using OpenAPI/Swagger specification.
76+
77+
```typescript
78+
const api = new RestApi(this, 'Api', {
79+
apiName: 'MyRestApi',
80+
stageName: 'dev',
81+
definitionFileName: 'openapi.yaml',
82+
authentication: auth,
83+
datastore: table,
84+
domainName: 'example.com',
85+
apiHostname: 'api',
86+
cors: true,
87+
});
88+
```
89+
90+
#### `SingleTableDatastore`
91+
92+
Sets up a DynamoDB table following the single-table design pattern.
93+
94+
```typescript
95+
const table = new SingleTableDatastore(this, 'Table', {
96+
tableName: 'MyTable',
97+
design: {
98+
primaryKey: {
99+
partitionKey: 'PK',
100+
sortKey: 'SK',
101+
},
102+
indexes: {
103+
GSI1: {
104+
partitionKey: 'GSI1PK',
105+
sortKey: 'GSI1SK',
106+
},
107+
},
108+
},
109+
ttlAttribute: 'expires',
110+
});
111+
```
112+
113+
#### `LambdaFunction`
114+
115+
Extended NodejsFunction with additional configurations and permissions.
116+
117+
```typescript
118+
const lambda = new LambdaFunction(this, 'MyFunction', {
119+
entry: 'path/to/handler.ts',
120+
timeout: Duration.seconds(30),
121+
environment: {
122+
TABLE_NAME: table.tableName,
123+
},
124+
});
125+
126+
// Grant permissions
127+
lambda.grantDatastoreReadWrite(table);
128+
lambda.grantUserpool(auth.userPool);
129+
```
130+
131+
#### `Workflow`
132+
133+
Creates AWS Step Functions state machine.
134+
135+
```typescript
136+
const workflow = new Workflow(this, 'MyWorkflow', {
137+
definitionFileName: 'workflow/definition.asl.json',
138+
});
139+
```
140+
141+
### Lambda Utilities (`src/lambda/`)
142+
143+
#### HTTP Handler
144+
145+
```typescript
146+
import { createHttpHandler } from 'cdk-serverless/lambda';
147+
148+
export const handler = createHttpHandler(async (ctx) => {
149+
return {
150+
statusCode: 200,
151+
body: JSON.stringify({ message: 'Hello World' }),
152+
};
153+
});
154+
```
155+
156+
#### OpenAPI Handler
157+
158+
```typescript
159+
import { createOpenApiHandler } from 'cdk-serverless/lambda';
160+
161+
export const handler = createOpenApiHandler<paths['/items']['get']>(async (ctx) => {
162+
// Type-safe access to parameters
163+
const limit = ctx.queryParams.limit;
164+
165+
return {
166+
items: [/* items */],
167+
};
168+
});
169+
```
170+
171+
#### AppSync (GraphQL) Handler
172+
173+
```typescript
174+
import { createAppSyncHandler } from 'cdk-serverless/lambda';
175+
176+
export const handler = createAppSyncHandler<GetItemsQuery, GetItemsResult>(async (ctx) => {
177+
const { limit } = ctx.arguments;
178+
179+
return {
180+
items: [/* items */],
181+
};
182+
});
183+
```
184+
185+
#### Error Handling
186+
187+
```typescript
188+
import { BadRequestError, NotFoundError } from 'cdk-serverless/lambda';
189+
190+
export const handler = createHttpHandler(async (ctx) => {
191+
if (!ctx.queryParams.id) {
192+
throw new BadRequestError('Missing ID parameter');
193+
}
194+
195+
const item = await getItem(ctx.queryParams.id);
196+
197+
if (!item) {
198+
throw new NotFoundError('Item not found');
199+
}
200+
201+
return item;
202+
});
203+
```
204+
205+
### Projen Integration (`src/projen/`)
206+
207+
#### ServerlessProject
208+
209+
```typescript
210+
import { ServerlessProject } from 'cdk-serverless/projen';
211+
212+
const project = new ServerlessProject({
213+
name: 'my-serverless-app',
214+
defaultReleaseBranch: 'main',
215+
});
216+
```
217+
218+
#### RestApi Project
219+
220+
```typescript
221+
import { RestApi } from 'cdk-serverless/projen';
222+
223+
const project = new ServerlessProject({
224+
name: 'my-api',
225+
defaultReleaseBranch: 'main',
226+
});
227+
228+
new RestApi(project, {
229+
apiName: 'MyApi',
230+
definitionFile: 'api.yaml',
231+
});
232+
```
233+
234+
#### GraphQL API Project
235+
236+
```typescript
237+
import { GraphQlApi } from 'cdk-serverless/projen';
238+
239+
const project = new ServerlessProject({
240+
name: 'my-graphql',
241+
defaultReleaseBranch: 'main',
242+
});
243+
244+
new GraphQlApi(project, {
245+
apiName: 'MyGraphQLApi',
246+
definitionFile: 'schema.graphql',
247+
});
248+
```
249+
250+
### Testing Utilities (`src/tests/`)
251+
252+
#### Lambda REST API Testing
253+
254+
```typescript
255+
import { LambdaRestUnitTest } from 'cdk-serverless/tests/lambda-test-utils';
256+
257+
describe('API Handler', () => {
258+
const test = new LambdaRestUnitTest(handler, {
259+
headers: {
260+
'Content-Type': 'application/json',
261+
},
262+
cognito: {
263+
username: 'test-user',
264+
265+
groups: ['admin'],
266+
},
267+
});
268+
269+
it('gets items successfully', async () => {
270+
const result = await test.call({
271+
path: '/items',
272+
method: 'GET',
273+
queryParams: { limit: '10' },
274+
});
275+
276+
expect(result.statusCode).toBe(200);
277+
expect(JSON.parse(result.body)).toHaveProperty('items');
278+
});
279+
});
280+
```
281+
282+
#### Lambda GraphQL Testing
283+
284+
```typescript
285+
import { LambdaGraphQLTest } from 'cdk-serverless/tests/lambda-test-utils';
286+
287+
describe('GraphQL Resolver', () => {
288+
const test = new LambdaGraphQLTest(handler, {
289+
cognito: {
290+
username: 'test-user',
291+
292+
groups: ['admin'],
293+
},
294+
});
295+
296+
it('resolves getItems query', async () => {
297+
const result = await test.call({
298+
fieldName: 'getItems',
299+
arguments: { limit: 10 },
300+
});
301+
302+
expect(result).toHaveProperty('items');
303+
});
304+
});
305+
```
306+
307+
#### Integration Testing
308+
309+
```typescript
310+
import { IntegTestUtil } from 'cdk-serverless/tests/integ-test-util';
311+
312+
describe('Integration Tests', () => {
313+
const util = new IntegTestUtil({
314+
region: 'us-east-1',
315+
apiOptions: {
316+
baseURL: 'https://api.example.com',
317+
},
318+
authOptions: {
319+
userPoolId: 'us-east-1_xxxxx',
320+
userPoolClientId: 'xxxxxxxx',
321+
identityPoolId: 'us-east-1:xxxxxxxx',
322+
},
323+
datastoreOptions: {
324+
tableName: 'MyTable',
325+
},
326+
});
327+
328+
beforeAll(async () => {
329+
await util.createUser('[email protected]', {}, ['admin']);
330+
});
331+
332+
afterAll(async () => {
333+
await util.cleanupItems();
334+
await util.removeUser('[email protected]');
335+
});
336+
337+
it('tests API with authenticated user', async () => {
338+
const client = await util.getAuthenticatedClient('[email protected]');
339+
340+
const response = await client.get('/items');
341+
expect(response.status).toBe(200);
342+
});
343+
});
344+
```
345+
346+
## Common Workflows
347+
348+
### Creating a New Serverless Project
349+
350+
1. Initialize a new ServerlessProject
351+
2. Add required constructs (RestApi, GraphQlApi, etc.)
352+
3. Run `npx projen` to generate files
353+
4. Implement Lambda handlers for API operations
354+
5. Deploy with `cdk deploy`
355+
356+
### Adding a New API Endpoint
357+
358+
1. Update OpenAPI/GraphQL schema
359+
2. Run `npx projen` to regenerate models
360+
3. Create Lambda handler for the new operation
361+
4. Update API construct with new handler
362+
363+
### Implementing Authentication
364+
365+
1. Add CognitoAuthentication construct
366+
2. Configure API to use the authentication
367+
3. Use authentication helpers in Lambda handlers
368+
369+
### Working with DynamoDB
370+
371+
1. Create SingleTableDatastore with desired schema
372+
2. Grant Lambda functions access to the table
373+
3. Use DynamoDB client in Lambda handlers
374+
375+
## Best Practices
376+
377+
1. **Single-table Design**: Use one DynamoDB table with carefully designed keys for all entities.
378+
2. **Type Safety**: Leverage TypeScript and generated types for API operations.
379+
3. **Error Handling**: Use the provided error classes for consistent error responses.
380+
4. **Testing**: Write unit tests with the provided testing utilities.
381+
5. **Authentication**: Properly secure all API endpoints using the authentication helpers.
382+
383+
## Common Pitfalls
384+
385+
1. **Incorrect Permissions**: Ensure Lambda functions have the necessary permissions to access resources.
386+
2. **Missing Environment Variables**: Set required environment variables for Lambda functions.
387+
3. **Improper Error Handling**: Use the provided error classes instead of throwing generic errors.
388+
4. **CDK Version Mismatches**: Ensure AWS CDK version is compatible with cdk-serverless.
389+
390+
## Conclusion
391+
392+
CDK Serverless provides a rich set of tools for building serverless applications on AWS. By understanding its components and usage patterns, you can efficiently generate code that follows best practices and integrates well with the library's features.

0 commit comments

Comments
 (0)