omgkit 2.2.0 → 2.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. package/README.md +3 -3
  2. package/package.json +1 -1
  3. package/plugin/skills/databases/database-management/SKILL.md +288 -0
  4. package/plugin/skills/databases/database-migration/SKILL.md +285 -0
  5. package/plugin/skills/databases/database-schema-design/SKILL.md +195 -0
  6. package/plugin/skills/databases/mongodb/SKILL.md +60 -776
  7. package/plugin/skills/databases/prisma/SKILL.md +53 -744
  8. package/plugin/skills/databases/redis/SKILL.md +53 -860
  9. package/plugin/skills/databases/supabase/SKILL.md +283 -0
  10. package/plugin/skills/devops/aws/SKILL.md +68 -672
  11. package/plugin/skills/devops/github-actions/SKILL.md +54 -657
  12. package/plugin/skills/devops/kubernetes/SKILL.md +67 -602
  13. package/plugin/skills/devops/performance-profiling/SKILL.md +59 -863
  14. package/plugin/skills/frameworks/django/SKILL.md +87 -853
  15. package/plugin/skills/frameworks/express/SKILL.md +95 -1301
  16. package/plugin/skills/frameworks/fastapi/SKILL.md +90 -1198
  17. package/plugin/skills/frameworks/laravel/SKILL.md +87 -1187
  18. package/plugin/skills/frameworks/nestjs/SKILL.md +106 -973
  19. package/plugin/skills/frameworks/react/SKILL.md +94 -962
  20. package/plugin/skills/frameworks/vue/SKILL.md +95 -1242
  21. package/plugin/skills/frontend/accessibility/SKILL.md +91 -1056
  22. package/plugin/skills/frontend/frontend-design/SKILL.md +69 -1262
  23. package/plugin/skills/frontend/responsive/SKILL.md +76 -799
  24. package/plugin/skills/frontend/shadcn-ui/SKILL.md +73 -921
  25. package/plugin/skills/frontend/tailwindcss/SKILL.md +60 -788
  26. package/plugin/skills/frontend/threejs/SKILL.md +72 -1266
  27. package/plugin/skills/languages/javascript/SKILL.md +106 -849
  28. package/plugin/skills/methodology/brainstorming/SKILL.md +70 -576
  29. package/plugin/skills/methodology/defense-in-depth/SKILL.md +79 -831
  30. package/plugin/skills/methodology/dispatching-parallel-agents/SKILL.md +81 -654
  31. package/plugin/skills/methodology/executing-plans/SKILL.md +86 -529
  32. package/plugin/skills/methodology/finishing-development-branch/SKILL.md +95 -586
  33. package/plugin/skills/methodology/problem-solving/SKILL.md +67 -681
  34. package/plugin/skills/methodology/receiving-code-review/SKILL.md +70 -533
  35. package/plugin/skills/methodology/requesting-code-review/SKILL.md +70 -610
  36. package/plugin/skills/methodology/root-cause-tracing/SKILL.md +70 -646
  37. package/plugin/skills/methodology/sequential-thinking/SKILL.md +70 -478
  38. package/plugin/skills/methodology/systematic-debugging/SKILL.md +66 -559
  39. package/plugin/skills/methodology/test-driven-development/SKILL.md +91 -752
  40. package/plugin/skills/methodology/testing-anti-patterns/SKILL.md +78 -687
  41. package/plugin/skills/methodology/token-optimization/SKILL.md +72 -602
  42. package/plugin/skills/methodology/verification-before-completion/SKILL.md +108 -529
  43. package/plugin/skills/methodology/writing-plans/SKILL.md +79 -566
  44. package/plugin/skills/omega/omega-architecture/SKILL.md +91 -752
  45. package/plugin/skills/omega/omega-coding/SKILL.md +161 -552
  46. package/plugin/skills/omega/omega-sprint/SKILL.md +132 -777
  47. package/plugin/skills/omega/omega-testing/SKILL.md +157 -845
  48. package/plugin/skills/omega/omega-thinking/SKILL.md +165 -606
  49. package/plugin/skills/security/better-auth/SKILL.md +46 -1034
  50. package/plugin/skills/security/oauth/SKILL.md +80 -934
  51. package/plugin/skills/security/owasp/SKILL.md +78 -862
  52. package/plugin/skills/testing/playwright/SKILL.md +77 -700
  53. package/plugin/skills/testing/pytest/SKILL.md +73 -811
  54. package/plugin/skills/testing/vitest/SKILL.md +60 -920
  55. package/plugin/skills/tools/document-processing/SKILL.md +111 -838
  56. package/plugin/skills/tools/image-processing/SKILL.md +126 -659
  57. package/plugin/skills/tools/mcp-development/SKILL.md +85 -758
  58. package/plugin/skills/tools/media-processing/SKILL.md +118 -735
  59. package/plugin/stdrules/SKILL_STANDARDS.md +490 -0
  60. package/plugin/skills/SKILL_STANDARDS.md +0 -743
@@ -1,709 +1,105 @@
1
1
  ---
2
- name: aws
3
- description: AWS cloud services with Lambda, S3, DynamoDB, ECS, and infrastructure as code patterns
4
- category: devops
5
- triggers:
6
- - aws
7
- - amazon web services
8
- - lambda
9
- - s3
10
- - dynamodb
11
- - ecs
12
- - cloudformation
13
- - cdk
2
+ name: Deploying to AWS
3
+ description: The agent implements AWS cloud solutions with Lambda, S3, DynamoDB, ECS, and CDK infrastructure as code. Use when building serverless functions, deploying containers, managing cloud storage, or defining infrastructure as code.
14
4
  ---
15
5
 
16
- # AWS
6
+ # Deploying to AWS
17
7
 
18
- Enterprise-grade **AWS cloud development** following industry best practices. This skill covers Lambda functions, S3 storage, DynamoDB, ECS containers, API Gateway, infrastructure as code with CDK, and production-ready patterns used by top engineering teams.
19
-
20
- ## Purpose
21
-
22
- Build scalable cloud applications on AWS:
23
-
24
- - Deploy serverless functions with Lambda
25
- - Store and retrieve data with S3 and DynamoDB
26
- - Run containerized applications with ECS
27
- - Build APIs with API Gateway
28
- - Implement infrastructure as code with CDK
29
- - Monitor applications with CloudWatch
30
- - Secure resources with IAM
31
-
32
- ## Features
33
-
34
- ### 1. Lambda Functions
8
+ ## Quick Start
35
9
 
36
10
  ```typescript
37
- // src/handlers/user.handler.ts
38
- import { APIGatewayProxyEvent, APIGatewayProxyResult, Context } from 'aws-lambda';
39
- import { DynamoDBClient } from '@aws-sdk/client-dynamodb';
40
- import { DynamoDBDocumentClient, GetCommand, PutCommand, QueryCommand } from '@aws-sdk/lib-dynamodb';
41
-
42
- const client = new DynamoDBClient({});
43
- const docClient = DynamoDBDocumentClient.from(client);
44
-
45
- const TABLE_NAME = process.env.USERS_TABLE!;
46
-
47
- interface User {
48
- id: string;
49
- email: string;
50
- name: string;
51
- createdAt: string;
52
- }
53
-
54
- // Response helpers
55
- const response = (statusCode: number, body: unknown): APIGatewayProxyResult => ({
56
- statusCode,
57
- headers: {
58
- 'Content-Type': 'application/json',
59
- 'Access-Control-Allow-Origin': '*',
60
- 'Access-Control-Allow-Credentials': true,
61
- },
62
- body: JSON.stringify(body),
63
- });
64
-
65
- const success = (data: unknown) => response(200, { data });
66
- const created = (data: unknown) => response(201, { data });
67
- const notFound = (message: string) => response(404, { error: { code: 'NOT_FOUND', message } });
68
- const badRequest = (message: string) => response(400, { error: { code: 'BAD_REQUEST', message } });
69
- const serverError = (error: Error) => response(500, { error: { code: 'SERVER_ERROR', message: error.message } });
70
-
71
- // GET /users/{id}
72
- export const getUser = async (event: APIGatewayProxyEvent): Promise<APIGatewayProxyResult> => {
73
- try {
74
- const userId = event.pathParameters?.id;
75
- if (!userId) return badRequest('User ID is required');
76
-
77
- const result = await docClient.send(new GetCommand({
78
- TableName: TABLE_NAME,
79
- Key: { id: userId },
80
- }));
81
-
82
- if (!result.Item) {
83
- return notFound(`User ${userId} not found`);
84
- }
85
-
86
- return success(result.Item);
87
- } catch (error) {
88
- console.error('Error getting user:', error);
89
- return serverError(error as Error);
90
- }
91
- };
92
-
93
- // POST /users
94
- export const createUser = async (event: APIGatewayProxyEvent): Promise<APIGatewayProxyResult> => {
95
- try {
96
- if (!event.body) return badRequest('Request body is required');
97
-
98
- const body = JSON.parse(event.body);
99
- const { email, name } = body;
100
-
101
- if (!email || !name) {
102
- return badRequest('Email and name are required');
103
- }
104
-
105
- const user: User = {
106
- id: crypto.randomUUID(),
107
- email: email.toLowerCase(),
108
- name,
109
- createdAt: new Date().toISOString(),
110
- };
111
-
112
- await docClient.send(new PutCommand({
113
- TableName: TABLE_NAME,
114
- Item: user,
115
- ConditionExpression: 'attribute_not_exists(id)',
116
- }));
117
-
118
- return created(user);
119
- } catch (error) {
120
- console.error('Error creating user:', error);
121
- return serverError(error as Error);
122
- }
11
+ // Lambda handler
12
+ import { APIGatewayProxyEvent, APIGatewayProxyResult } from 'aws-lambda';
13
+
14
+ export const handler = async (event: APIGatewayProxyEvent): Promise<APIGatewayProxyResult> => {
15
+ const { id } = event.pathParameters || {};
16
+ return {
17
+ statusCode: 200,
18
+ headers: { 'Content-Type': 'application/json' },
19
+ body: JSON.stringify({ id, message: 'Success' }),
20
+ };
123
21
  };
22
+ ```
124
23
 
125
- // GET /users
126
- export const listUsers = async (event: APIGatewayProxyEvent): Promise<APIGatewayProxyResult> => {
127
- try {
128
- const limit = parseInt(event.queryStringParameters?.limit || '20');
129
- const lastKey = event.queryStringParameters?.cursor;
130
-
131
- const params: any = {
132
- TableName: TABLE_NAME,
133
- Limit: limit,
134
- };
135
-
136
- if (lastKey) {
137
- params.ExclusiveStartKey = JSON.parse(Buffer.from(lastKey, 'base64').toString());
138
- }
24
+ ```bash
25
+ # Deploy with CDK
26
+ npx cdk deploy --all
27
+ ```
139
28
 
140
- const result = await docClient.send(new QueryCommand(params));
29
+ ## Features
141
30
 
142
- const cursor = result.LastEvaluatedKey
143
- ? Buffer.from(JSON.stringify(result.LastEvaluatedKey)).toString('base64')
144
- : null;
31
+ | Feature | Description | Guide |
32
+ |---------|-------------|-------|
33
+ | Lambda | Serverless function execution | Use for APIs, event processing, scheduled tasks |
34
+ | S3 | Object storage with presigned URLs | Store files, serve static assets, data lakes |
35
+ | DynamoDB | NoSQL database with single-digit ms latency | Design single-table schemas with GSIs |
36
+ | ECS/Fargate | Container orchestration | Run Docker containers without managing servers |
37
+ | API Gateway | REST and WebSocket API management | Throttling, auth, request validation |
38
+ | CDK | Infrastructure as TypeScript code | Define stacks, manage deployments programmatically |
145
39
 
146
- return success({
147
- items: result.Items,
148
- cursor,
149
- hasMore: !!result.LastEvaluatedKey,
150
- });
151
- } catch (error) {
152
- console.error('Error listing users:', error);
153
- return serverError(error as Error);
154
- }
155
- };
156
- ```
40
+ ## Common Patterns
157
41
 
158
- ### 2. S3 Operations
42
+ ### S3 Presigned Upload URL
159
43
 
160
44
  ```typescript
161
- // src/services/s3.service.ts
162
- import {
163
- S3Client,
164
- PutObjectCommand,
165
- GetObjectCommand,
166
- DeleteObjectCommand,
167
- ListObjectsV2Command,
168
- CopyObjectCommand,
169
- } from '@aws-sdk/client-s3';
45
+ import { S3Client, PutObjectCommand } from '@aws-sdk/client-s3';
170
46
  import { getSignedUrl } from '@aws-sdk/s3-request-presigner';
171
- import { Readable } from 'stream';
172
-
173
- const s3Client = new S3Client({ region: process.env.AWS_REGION });
174
- const BUCKET = process.env.S3_BUCKET!;
175
-
176
- export class S3Service {
177
- // Upload file
178
- async upload(key: string, body: Buffer | Readable, contentType: string): Promise<string> {
179
- await s3Client.send(new PutObjectCommand({
180
- Bucket: BUCKET,
181
- Key: key,
182
- Body: body,
183
- ContentType: contentType,
184
- }));
185
-
186
- return `s3://${BUCKET}/${key}`;
187
- }
188
-
189
- // Upload with metadata
190
- async uploadWithMetadata(
191
- key: string,
192
- body: Buffer,
193
- options: {
194
- contentType: string;
195
- metadata?: Record<string, string>;
196
- tags?: Record<string, string>;
197
- }
198
- ): Promise<string> {
199
- await s3Client.send(new PutObjectCommand({
200
- Bucket: BUCKET,
201
- Key: key,
202
- Body: body,
203
- ContentType: options.contentType,
204
- Metadata: options.metadata,
205
- Tagging: options.tags
206
- ? Object.entries(options.tags).map(([k, v]) => `${k}=${v}`).join('&')
207
- : undefined,
208
- }));
209
-
210
- return `https://${BUCKET}.s3.amazonaws.com/${key}`;
211
- }
212
-
213
- // Get file
214
- async get(key: string): Promise<Buffer> {
215
- const response = await s3Client.send(new GetObjectCommand({
216
- Bucket: BUCKET,
217
- Key: key,
218
- }));
219
-
220
- const chunks: Buffer[] = [];
221
- for await (const chunk of response.Body as Readable) {
222
- chunks.push(chunk);
223
- }
224
- return Buffer.concat(chunks);
225
- }
226
-
227
- // Generate presigned upload URL
228
- async getUploadUrl(key: string, contentType: string, expiresIn = 3600): Promise<string> {
229
- const command = new PutObjectCommand({
230
- Bucket: BUCKET,
231
- Key: key,
232
- ContentType: contentType,
233
- });
234
-
235
- return getSignedUrl(s3Client, command, { expiresIn });
236
- }
237
-
238
- // Generate presigned download URL
239
- async getDownloadUrl(key: string, expiresIn = 3600): Promise<string> {
240
- const command = new GetObjectCommand({
241
- Bucket: BUCKET,
242
- Key: key,
243
- });
244
-
245
- return getSignedUrl(s3Client, command, { expiresIn });
246
- }
247
-
248
- // List objects with prefix
249
- async list(prefix: string, maxKeys = 1000): Promise<string[]> {
250
- const response = await s3Client.send(new ListObjectsV2Command({
251
- Bucket: BUCKET,
252
- Prefix: prefix,
253
- MaxKeys: maxKeys,
254
- }));
255
-
256
- return response.Contents?.map(obj => obj.Key!) || [];
257
- }
258
47
 
259
- // Delete file
260
- async delete(key: string): Promise<void> {
261
- await s3Client.send(new DeleteObjectCommand({
262
- Bucket: BUCKET,
263
- Key: key,
264
- }));
265
- }
48
+ const s3 = new S3Client({});
266
49
 
267
- // Copy file
268
- async copy(sourceKey: string, destKey: string): Promise<void> {
269
- await s3Client.send(new CopyObjectCommand({
270
- Bucket: BUCKET,
271
- CopySource: `${BUCKET}/${sourceKey}`,
272
- Key: destKey,
273
- }));
274
- }
50
+ async function getUploadUrl(key: string, contentType: string): Promise<string> {
51
+ const command = new PutObjectCommand({ Bucket: process.env.BUCKET!, Key: key, ContentType: contentType });
52
+ return getSignedUrl(s3, command, { expiresIn: 3600 });
275
53
  }
276
54
  ```
277
55
 
278
- ### 3. DynamoDB Patterns
56
+ ### DynamoDB Single-Table Pattern
279
57
 
280
58
  ```typescript
281
- // src/services/dynamodb.service.ts
282
- import { DynamoDBClient } from '@aws-sdk/client-dynamodb';
283
- import {
284
- DynamoDBDocumentClient,
285
- GetCommand,
286
- PutCommand,
287
- UpdateCommand,
288
- DeleteCommand,
289
- QueryCommand,
290
- BatchWriteCommand,
291
- TransactWriteCommand,
292
- } from '@aws-sdk/lib-dynamodb';
293
-
294
- const client = new DynamoDBClient({});
295
- const docClient = DynamoDBDocumentClient.from(client, {
296
- marshallOptions: { removeUndefinedValues: true },
297
- });
298
-
299
- export class DynamoDBService<T extends { pk: string; sk: string }> {
300
- constructor(private tableName: string) {}
301
-
302
- async get(pk: string, sk: string): Promise<T | null> {
303
- const result = await docClient.send(new GetCommand({
304
- TableName: this.tableName,
305
- Key: { pk, sk },
306
- }));
307
- return (result.Item as T) || null;
308
- }
309
-
310
- async put(item: T): Promise<T> {
311
- await docClient.send(new PutCommand({
312
- TableName: this.tableName,
313
- Item: item,
314
- }));
315
- return item;
316
- }
317
-
318
- async update(pk: string, sk: string, updates: Partial<T>): Promise<T> {
319
- const updateExpressions: string[] = [];
320
- const expressionNames: Record<string, string> = {};
321
- const expressionValues: Record<string, unknown> = {};
322
-
323
- Object.entries(updates).forEach(([key, value], index) => {
324
- if (key !== 'pk' && key !== 'sk') {
325
- updateExpressions.push(`#attr${index} = :val${index}`);
326
- expressionNames[`#attr${index}`] = key;
327
- expressionValues[`:val${index}`] = value;
328
- }
329
- });
330
-
331
- const result = await docClient.send(new UpdateCommand({
332
- TableName: this.tableName,
333
- Key: { pk, sk },
334
- UpdateExpression: `SET ${updateExpressions.join(', ')}`,
335
- ExpressionAttributeNames: expressionNames,
336
- ExpressionAttributeValues: expressionValues,
337
- ReturnValues: 'ALL_NEW',
338
- }));
339
-
340
- return result.Attributes as T;
341
- }
342
-
343
- async delete(pk: string, sk: string): Promise<void> {
344
- await docClient.send(new DeleteCommand({
345
- TableName: this.tableName,
346
- Key: { pk, sk },
347
- }));
348
- }
349
-
350
- async query(
351
- pk: string,
352
- options?: {
353
- skPrefix?: string;
354
- limit?: number;
355
- scanForward?: boolean;
356
- }
357
- ): Promise<T[]> {
358
- const params: any = {
359
- TableName: this.tableName,
360
- KeyConditionExpression: 'pk = :pk',
361
- ExpressionAttributeValues: { ':pk': pk },
362
- Limit: options?.limit,
363
- ScanIndexForward: options?.scanForward ?? true,
364
- };
365
-
366
- if (options?.skPrefix) {
367
- params.KeyConditionExpression += ' AND begins_with(sk, :skPrefix)';
368
- params.ExpressionAttributeValues[':skPrefix'] = options.skPrefix;
369
- }
370
-
371
- const result = await docClient.send(new QueryCommand(params));
372
- return (result.Items as T[]) || [];
373
- }
374
-
375
- async batchWrite(items: T[]): Promise<void> {
376
- const chunks = this.chunkArray(items, 25);
377
-
378
- for (const chunk of chunks) {
379
- await docClient.send(new BatchWriteCommand({
380
- RequestItems: {
381
- [this.tableName]: chunk.map(item => ({
382
- PutRequest: { Item: item },
383
- })),
384
- },
385
- }));
386
- }
387
- }
388
-
389
- async transactWrite(operations: Array<{ type: 'put' | 'delete'; item: T }>): Promise<void> {
390
- await docClient.send(new TransactWriteCommand({
391
- TransactItems: operations.map(op => {
392
- if (op.type === 'put') {
393
- return { Put: { TableName: this.tableName, Item: op.item } };
394
- }
395
- return { Delete: { TableName: this.tableName, Key: { pk: op.item.pk, sk: op.item.sk } } };
396
- }),
397
- }));
398
- }
399
-
400
- private chunkArray<U>(array: U[], size: number): U[][] {
401
- const chunks: U[][] = [];
402
- for (let i = 0; i < array.length; i += size) {
403
- chunks.push(array.slice(i, i + size));
404
- }
405
- return chunks;
406
- }
59
+ import { DynamoDBDocumentClient, QueryCommand } from '@aws-sdk/lib-dynamodb';
60
+
61
+ // pk: USER#123, sk: PROFILE | ORDER#timestamp
62
+ async function getUserWithOrders(userId: string) {
63
+ const result = await docClient.send(new QueryCommand({
64
+ TableName: process.env.TABLE!,
65
+ KeyConditionExpression: 'pk = :pk',
66
+ ExpressionAttributeValues: { ':pk': `USER#${userId}` },
67
+ }));
68
+ return result.Items;
407
69
  }
408
70
  ```
409
71
 
410
- ### 4. CDK Infrastructure
72
+ ### CDK Stack Definition
411
73
 
412
74
  ```typescript
413
- // lib/app-stack.ts
414
75
  import * as cdk from 'aws-cdk-lib';
415
76
  import * as lambda from 'aws-cdk-lib/aws-lambda';
416
- import * as apigateway from 'aws-cdk-lib/aws-apigateway';
417
77
  import * as dynamodb from 'aws-cdk-lib/aws-dynamodb';
418
- import * as s3 from 'aws-cdk-lib/aws-s3';
419
- import * as iam from 'aws-cdk-lib/aws-iam';
420
- import { Construct } from 'constructs';
421
- import { NodejsFunction } from 'aws-cdk-lib/aws-lambda-nodejs';
422
-
423
- export class AppStack extends cdk.Stack {
424
- constructor(scope: Construct, id: string, props?: cdk.StackProps) {
425
- super(scope, id, props);
426
-
427
- // DynamoDB Table
428
- const usersTable = new dynamodb.Table(this, 'UsersTable', {
429
- partitionKey: { name: 'pk', type: dynamodb.AttributeType.STRING },
430
- sortKey: { name: 'sk', type: dynamodb.AttributeType.STRING },
431
- billingMode: dynamodb.BillingMode.PAY_PER_REQUEST,
432
- pointInTimeRecovery: true,
433
- removalPolicy: cdk.RemovalPolicy.RETAIN,
434
- });
435
-
436
- usersTable.addGlobalSecondaryIndex({
437
- indexName: 'GSI1',
438
- partitionKey: { name: 'gsi1pk', type: dynamodb.AttributeType.STRING },
439
- sortKey: { name: 'gsi1sk', type: dynamodb.AttributeType.STRING },
440
- projectionType: dynamodb.ProjectionType.ALL,
441
- });
442
-
443
- // S3 Bucket
444
- const bucket = new s3.Bucket(this, 'AssetsBucket', {
445
- encryption: s3.BucketEncryption.S3_MANAGED,
446
- blockPublicAccess: s3.BlockPublicAccess.BLOCK_ALL,
447
- versioned: true,
448
- lifecycleRules: [
449
- {
450
- expiration: cdk.Duration.days(365),
451
- noncurrentVersionExpiration: cdk.Duration.days(30),
452
- },
453
- ],
454
- });
455
-
456
- // Lambda Functions
457
- const getUserFn = new NodejsFunction(this, 'GetUserFunction', {
458
- entry: 'src/handlers/user.handler.ts',
459
- handler: 'getUser',
460
- runtime: lambda.Runtime.NODEJS_20_X,
461
- timeout: cdk.Duration.seconds(30),
462
- memorySize: 256,
463
- environment: {
464
- USERS_TABLE: usersTable.tableName,
465
- S3_BUCKET: bucket.bucketName,
466
- },
467
- tracing: lambda.Tracing.ACTIVE,
468
- });
469
-
470
- const createUserFn = new NodejsFunction(this, 'CreateUserFunction', {
471
- entry: 'src/handlers/user.handler.ts',
472
- handler: 'createUser',
473
- runtime: lambda.Runtime.NODEJS_20_X,
474
- timeout: cdk.Duration.seconds(30),
475
- memorySize: 256,
476
- environment: {
477
- USERS_TABLE: usersTable.tableName,
478
- },
479
- });
480
-
481
- // Grant permissions
482
- usersTable.grantReadData(getUserFn);
483
- usersTable.grantReadWriteData(createUserFn);
484
- bucket.grantRead(getUserFn);
485
-
486
- // API Gateway
487
- const api = new apigateway.RestApi(this, 'UsersApi', {
488
- restApiName: 'Users Service',
489
- deployOptions: {
490
- stageName: 'prod',
491
- throttlingRateLimit: 1000,
492
- throttlingBurstLimit: 500,
493
- },
494
- defaultCorsPreflightOptions: {
495
- allowOrigins: apigateway.Cors.ALL_ORIGINS,
496
- allowMethods: apigateway.Cors.ALL_METHODS,
497
- },
498
- });
499
-
500
- const users = api.root.addResource('users');
501
- users.addMethod('GET', new apigateway.LambdaIntegration(getUserFn));
502
- users.addMethod('POST', new apigateway.LambdaIntegration(createUserFn));
503
-
504
- const user = users.addResource('{id}');
505
- user.addMethod('GET', new apigateway.LambdaIntegration(getUserFn));
506
-
507
- // Outputs
508
- new cdk.CfnOutput(this, 'ApiUrl', { value: api.url });
509
- new cdk.CfnOutput(this, 'BucketName', { value: bucket.bucketName });
510
- }
511
- }
512
- ```
513
-
514
- ### 5. ECS Container Deployment
515
-
516
- ```typescript
517
- // lib/ecs-stack.ts
518
- import * as cdk from 'aws-cdk-lib';
519
- import * as ec2 from 'aws-cdk-lib/aws-ec2';
520
- import * as ecs from 'aws-cdk-lib/aws-ecs';
521
- import * as ecsPatterns from 'aws-cdk-lib/aws-ecs-patterns';
522
- import * as ecr from 'aws-cdk-lib/aws-ecr';
523
- import { Construct } from 'constructs';
524
-
525
- export class EcsStack extends cdk.Stack {
526
- constructor(scope: Construct, id: string, props?: cdk.StackProps) {
527
- super(scope, id, props);
528
-
529
- // VPC
530
- const vpc = new ec2.Vpc(this, 'AppVpc', {
531
- maxAzs: 2,
532
- natGateways: 1,
533
- });
534
-
535
- // ECS Cluster
536
- const cluster = new ecs.Cluster(this, 'AppCluster', {
537
- vpc,
538
- containerInsights: true,
539
- });
540
-
541
- // Fargate Service with ALB
542
- const service = new ecsPatterns.ApplicationLoadBalancedFargateService(
543
- this,
544
- 'AppService',
545
- {
546
- cluster,
547
- taskImageOptions: {
548
- image: ecs.ContainerImage.fromRegistry('nginx:latest'),
549
- containerPort: 80,
550
- environment: {
551
- NODE_ENV: 'production',
552
- },
553
- },
554
- desiredCount: 2,
555
- cpu: 256,
556
- memoryLimitMiB: 512,
557
- publicLoadBalancer: true,
558
- healthCheckGracePeriod: cdk.Duration.seconds(60),
559
- }
560
- );
561
-
562
- // Auto Scaling
563
- const scaling = service.service.autoScaleTaskCount({
564
- minCapacity: 2,
565
- maxCapacity: 10,
566
- });
567
-
568
- scaling.scaleOnCpuUtilization('CpuScaling', {
569
- targetUtilizationPercent: 70,
570
- scaleInCooldown: cdk.Duration.seconds(60),
571
- scaleOutCooldown: cdk.Duration.seconds(60),
572
- });
573
-
574
- scaling.scaleOnMemoryUtilization('MemoryScaling', {
575
- targetUtilizationPercent: 80,
576
- });
577
- }
578
- }
579
- ```
580
78
 
581
- ### 6. SQS and SNS Integration
582
-
583
- ```typescript
584
- // src/services/messaging.service.ts
585
- import { SQSClient, SendMessageCommand, ReceiveMessageCommand, DeleteMessageCommand } from '@aws-sdk/client-sqs';
586
- import { SNSClient, PublishCommand } from '@aws-sdk/client-sns';
587
-
588
- const sqsClient = new SQSClient({});
589
- const snsClient = new SNSClient({});
590
-
591
- export class MessagingService {
592
- // Send message to SQS
593
- async sendToQueue(queueUrl: string, message: unknown, delaySeconds = 0): Promise<string> {
594
- const result = await sqsClient.send(new SendMessageCommand({
595
- QueueUrl: queueUrl,
596
- MessageBody: JSON.stringify(message),
597
- DelaySeconds: delaySeconds,
598
- }));
599
- return result.MessageId!;
600
- }
601
-
602
- // Receive messages from SQS
603
- async receiveFromQueue(queueUrl: string, maxMessages = 10): Promise<Array<{ id: string; body: unknown; receiptHandle: string }>> {
604
- const result = await sqsClient.send(new ReceiveMessageCommand({
605
- QueueUrl: queueUrl,
606
- MaxNumberOfMessages: maxMessages,
607
- WaitTimeSeconds: 20,
608
- }));
609
-
610
- return (result.Messages || []).map(msg => ({
611
- id: msg.MessageId!,
612
- body: JSON.parse(msg.Body!),
613
- receiptHandle: msg.ReceiptHandle!,
614
- }));
615
- }
616
-
617
- // Delete message from SQS
618
- async deleteFromQueue(queueUrl: string, receiptHandle: string): Promise<void> {
619
- await sqsClient.send(new DeleteMessageCommand({
620
- QueueUrl: queueUrl,
621
- ReceiptHandle: receiptHandle,
622
- }));
623
- }
624
-
625
- // Publish to SNS
626
- async publishToTopic(topicArn: string, message: unknown, subject?: string): Promise<string> {
627
- const result = await snsClient.send(new PublishCommand({
628
- TopicArn: topicArn,
629
- Message: JSON.stringify(message),
630
- Subject: subject,
631
- }));
632
- return result.MessageId!;
633
- }
634
- }
635
- ```
636
-
637
- ## Use Cases
638
-
639
- ### File Upload with Presigned URL
640
-
641
- ```typescript
642
- // Lambda handler for file upload
643
- export const getUploadUrl = async (event: APIGatewayProxyEvent): Promise<APIGatewayProxyResult> => {
644
- const { filename, contentType } = JSON.parse(event.body || '{}');
645
- const userId = event.requestContext.authorizer?.claims?.sub;
646
-
647
- const key = `uploads/${userId}/${Date.now()}-${filename}`;
648
- const s3Service = new S3Service();
649
- const uploadUrl = await s3Service.getUploadUrl(key, contentType);
650
-
651
- return success({ uploadUrl, key });
652
- };
653
- ```
654
-
655
- ### Event-Driven Processing
656
-
657
- ```typescript
658
- // SQS Event Handler
659
- import { SQSHandler, SQSEvent } from 'aws-lambda';
79
+ const table = new dynamodb.Table(this, 'Table', {
80
+ partitionKey: { name: 'pk', type: dynamodb.AttributeType.STRING },
81
+ sortKey: { name: 'sk', type: dynamodb.AttributeType.STRING },
82
+ billingMode: dynamodb.BillingMode.PAY_PER_REQUEST,
83
+ });
660
84
 
661
- export const processQueue: SQSHandler = async (event: SQSEvent) => {
662
- for (const record of event.Records) {
663
- const message = JSON.parse(record.body);
85
+ const fn = new lambda.Function(this, 'Handler', {
86
+ runtime: lambda.Runtime.NODEJS_20_X,
87
+ handler: 'index.handler',
88
+ code: lambda.Code.fromAsset('dist'),
89
+ environment: { TABLE_NAME: table.tableName },
90
+ });
664
91
 
665
- try {
666
- await processMessage(message);
667
- } catch (error) {
668
- console.error('Failed to process message:', error);
669
- throw error; // Message returns to queue
670
- }
671
- }
672
- };
92
+ table.grantReadWriteData(fn);
673
93
  ```
674
94
 
675
95
  ## Best Practices
676
96
 
677
- ### Do's
678
-
679
- - Use IAM roles instead of access keys
680
- - Enable encryption at rest and in transit
681
- - Use VPC for sensitive workloads
682
- - Implement proper error handling and retries
683
- - Use CloudWatch for monitoring and alerting
684
- - Tag all resources for cost tracking
685
- - Use infrastructure as code (CDK/CloudFormation)
686
- - Implement least privilege access
687
- - Use environment variables for configuration
688
- - Enable X-Ray tracing for debugging
689
-
690
- ### Don'ts
691
-
692
- - Don't hardcode credentials in code
693
- - Don't use root account for deployments
694
- - Don't expose S3 buckets publicly
695
- - Don't skip encryption for sensitive data
696
- - Don't ignore CloudWatch alarms
697
- - Don't over-provision resources
698
- - Don't skip VPC for production workloads
699
- - Don't use synchronous invocations for long tasks
700
- - Don't ignore cost optimization
701
- - Don't skip backup and disaster recovery
702
-
703
- ## References
704
-
705
- - [AWS Documentation](https://docs.aws.amazon.com/)
706
- - [AWS CDK Documentation](https://docs.aws.amazon.com/cdk/)
707
- - [AWS SDK for JavaScript](https://docs.aws.amazon.com/sdk-for-javascript/)
708
- - [AWS Well-Architected Framework](https://aws.amazon.com/architecture/well-architected/)
709
- - [AWS Best Practices](https://aws.amazon.com/architecture/best-practices/)
97
+ | Do | Avoid |
98
+ |----|-------|
99
+ | Use IAM roles, never access keys in code | Hardcoding credentials or secrets |
100
+ | Enable encryption at rest and in transit | Exposing S3 buckets publicly |
101
+ | Tag resources for cost tracking | Over-provisioning resources |
102
+ | Use environment variables for config | Skipping CloudWatch alarms |
103
+ | Implement least-privilege IAM policies | Using root account for deployments |
104
+ | Enable X-Ray tracing for debugging | Synchronous invocations for long tasks |
105
+ | Use VPC for sensitive workloads | Ignoring backup and disaster recovery |