src/skills/infra-platform-aws-sdk/SKILL.md
AWS SDK v3 for TypeScript — modular clients, command pattern, S3, DynamoDB, SQS, Lambda, SNS, Secrets Manager
npx skillsauth add agents-inc/skills infra-platform-aws-sdkInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Quick Guide: AWS SDK v3 for JavaScript/TypeScript uses modular packages (
@aws-sdk/client-*) with a command pattern: create a client, instantiate a command, callclient.send(command). Import only the services you need for tree-shaking. UseDynamoDBDocumentClientfor native JS types. UsegetSignedUrlfrom@aws-sdk/s3-request-presignerfor presigned URLs. Handle errors withinstanceofspecific exception classes. Use built-in paginators (paginate*) withfor await...of.
<critical_requirements>
All code must follow project conventions in CLAUDE.md (kebab-case, named exports, import ordering,
import type, named constants)
(You MUST use AWS SDK v3 modular packages (@aws-sdk/client-*) — NEVER the monolithic aws-sdk v2 package)
(You MUST use the command pattern: client.send(new XxxCommand({...})) — NEVER call methods directly on the client)
(You MUST use DynamoDBDocumentClient from @aws-sdk/lib-dynamodb for DynamoDB — it auto-marshalls native JS types)
(You MUST handle errors with instanceof specific exception classes — NEVER catch generic Error and check .code)
(You MUST use built-in paginators (paginate* functions) for paginated APIs — NEVER manually track continuation tokens)
</critical_requirements>
Auto-detection: AWS SDK, @aws-sdk/client, S3Client, DynamoDBClient, DynamoDBDocumentClient, SQSClient, LambdaClient, SNSClient, SecretsManagerClient, PutObjectCommand, GetObjectCommand, GetCommand, PutCommand, QueryCommand, SendMessageCommand, InvokeCommand, GetSecretValueCommand, getSignedUrl, s3-request-presigner, credential-providers, fromEnv, fromIni, paginateListObjectsV2, aws-sdk-client-mock
When to use:
When NOT to use:
Key patterns covered:
client.send(new Command({...})))DynamoDBDocumentClient with Get, Put, Query, Update, Deleteinstanceof exception classes and $metadataAWS SDK v3 is a ground-up rewrite of the v2 SDK for modern JavaScript/TypeScript. The core design principles:
Modular packages — Each service is a separate npm package (@aws-sdk/client-s3, @aws-sdk/client-dynamodb). Import only what you use. This reduces bundle size by up to 90% compared to the monolithic v2 aws-sdk package.
Command pattern — Every API call is a Command object sent through a Client. This enables middleware, type safety, and testability. The client handles serialization, signing, retries, and deserialization.
First-class TypeScript — Every command input and output is fully typed. Use the types to avoid runtime errors.
Middleware stack — Customize request/response handling at various stages (serialize, build, finalize, deserialize) without monkey-patching.
Built-in pagination — Paginator functions return async iterators, eliminating manual token tracking.
When to use AWS SDK v3:
When NOT to use:
Every AWS service follows the same pattern: import the client and command, create a client instance, send the command.
import { S3Client, PutObjectCommand } from "@aws-sdk/client-s3";
const s3 = new S3Client({ region: "us-east-1" });
await s3.send(
new PutObjectCommand({
Bucket: "my-bucket",
Key: "data.json",
Body: JSON.stringify({ hello: "world" }),
ContentType: "application/json",
}),
);
Why good: modular import keeps bundle small, command pattern enables middleware and type safety, region is explicit
Create clients once and reuse them — they manage connection pooling internally. In Lambda, create clients outside the handler for connection reuse across invocations.
See examples/core.md for client reuse patterns and configuration options.
S3 is the most commonly used service. Key operations: PutObject, GetObject, DeleteObject, ListObjectsV2, and presigned URLs via @aws-sdk/s3-request-presigner.
import { GetObjectCommand, NoSuchKey } from "@aws-sdk/client-s3";
const response = await s3.send(
new GetObjectCommand({
Bucket: "my-bucket",
Key: "data.json",
}),
);
const body = await response.Body?.transformToString();
For presigned URLs, use the separate @aws-sdk/s3-request-presigner package:
import { getSignedUrl } from "@aws-sdk/s3-request-presigner";
const PRESIGN_EXPIRY_SECONDS = 3_600;
const url = await getSignedUrl(
s3,
new GetObjectCommand({
Bucket: "my-bucket",
Key: "file.pdf",
}),
{ expiresIn: PRESIGN_EXPIRY_SECONDS },
);
See examples/core.md for upload, download, delete, list, and streaming patterns.
Use DynamoDBDocumentClient from @aws-sdk/lib-dynamodb — it automatically marshalls/unmarshalls between native JS types and DynamoDB's AttributeValue format.
import { DynamoDBClient } from "@aws-sdk/client-dynamodb";
import {
DynamoDBDocumentClient,
GetCommand,
PutCommand,
} from "@aws-sdk/lib-dynamodb";
const ddbDocClient = DynamoDBDocumentClient.from(new DynamoDBClient({}));
const { Item } = await ddbDocClient.send(
new GetCommand({
TableName: "users",
Key: { userId: "abc-123" },
}),
);
Why good: no manual marshall()/unmarshall() calls, native JS objects in and out, full type safety
Gotcha: Import commands from @aws-sdk/lib-dynamodb (not @aws-sdk/client-dynamodb) when using the document client — the lib-dynamodb commands accept native JS types.
See examples/core.md for Put, Query, Update, Delete, and batch operations.
AWS SDK v3 errors extend service-specific base classes (e.g., S3ServiceException). Use instanceof for typed error handling.
import {
GetObjectCommand,
NoSuchKey,
S3ServiceException,
} from "@aws-sdk/client-s3";
try {
await s3.send(new GetObjectCommand({ Bucket: "b", Key: "k" }));
} catch (error) {
if (error instanceof NoSuchKey) {
// Typed: error.name === "NoSuchKey", error.$metadata.httpStatusCode === 404
return null;
}
if (error instanceof S3ServiceException) {
// Any S3 service error — check error.$metadata.httpStatusCode
throw error;
}
throw error; // Non-AWS error (network, etc.)
}
Why good: instanceof gives TypeScript type narrowing, exception classes are exported from the client package, $metadata provides HTTP status and request ID for debugging
See examples/core.md for the full error handling decision tree and retry patterns.
Use built-in paginator functions for any paginated API. They return async iterators that handle continuation tokens automatically.
import { paginateListObjectsV2 } from "@aws-sdk/client-s3";
for await (const page of paginateListObjectsV2(
{ client: s3 },
{ Bucket: "my-bucket" },
)) {
// page.Contents is an array of objects for this page
}
Why good: no manual token tracking, clean for await...of loop, handles all edge cases (empty pages, token format)
See examples/core.md for full S3 list, DynamoDB pagination, and good/bad comparison with manual token tracking.
SQS uses SendMessageCommand, ReceiveMessageCommand, and DeleteMessageCommand. Always delete messages after processing.
import { SQSClient, SendMessageCommand } from "@aws-sdk/client-sqs";
const sqs = new SQSClient({});
await sqs.send(
new SendMessageCommand({
QueueUrl: QUEUE_URL,
MessageBody: JSON.stringify({ orderId: "order-123" }),
}),
);
See examples/messaging.md for receive/delete, long polling, FIFO queues, and dead-letter patterns.
SNS publishes messages to topics. Subscribers receive messages on their configured endpoints.
import { SNSClient, PublishCommand } from "@aws-sdk/client-sns";
const sns = new SNSClient({});
await sns.send(
new PublishCommand({
TopicArn: TOPIC_ARN,
Message: JSON.stringify({ event: "order.created", orderId: "order-123" }),
MessageAttributes: {
eventType: { DataType: "String", StringValue: "order.created" },
},
}),
);
See examples/messaging.md for topic management and message filtering.
Invoke Lambda functions synchronously or asynchronously. Retrieve secrets from Secrets Manager with caching.
import { LambdaClient, InvokeCommand } from "@aws-sdk/client-lambda";
const lambda = new LambdaClient({});
const response = await lambda.send(
new InvokeCommand({
FunctionName: "process-order",
InvocationType: "RequestResponse", // synchronous
Payload: JSON.stringify({ orderId: "order-123" }),
}),
);
const result = JSON.parse(new TextDecoder().decode(response.Payload));
See examples/advanced.md for async invocation, Secrets Manager retrieval, and caching patterns.
</patterns><decision_framework>
Are you working with DynamoDB?
|
+-- Need native JS objects (recommended) --> DynamoDBDocumentClient from @aws-sdk/lib-dynamodb
| +-- Import Get/Put/Query/Update/Delete Commands from @aws-sdk/lib-dynamodb
|
+-- Need raw AttributeValue format --> DynamoDBClient from @aws-sdk/client-dynamodb
+-- Import commands from @aws-sdk/client-dynamodb
+-- Manually marshall/unmarshall with @aws-sdk/util-dynamodb
Do you need the Lambda response immediately?
|
+-- YES --> InvocationType: "RequestResponse" (synchronous, waits for result)
|
+-- NO --> InvocationType: "Event" (async, returns immediately, 3 retries)
Where is this code running?
|
+-- Lambda / ECS / EC2 --> Default chain (auto-detects IAM role) — no config needed
|
+-- Local development --> fromIni() (reads ~/.aws/credentials) or fromEnv()
|
+-- CI/CD pipeline --> fromEnv() with AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY
|
+-- Cross-account access --> fromTemporaryCredentials() with STS AssumeRole
</decision_framework>
<red_flags>
High Priority Issues:
aws-sdk v2 package — it ships the entire SDK (~70 MB). Use modular @aws-sdk/client-* packages.s3.getObject()) — use the command pattern: s3.send(new GetObjectCommand({...})).DynamoDBClient commands with manual marshall()/unmarshall() — use DynamoDBDocumentClient from @aws-sdk/lib-dynamodb..code string comparison (v2 style) — use instanceof with typed exception classes.paginate* functions with for await...of.Medium Priority Issues:
@aws-sdk/client-dynamodb and @aws-sdk/lib-dynamodb command imports — pick one approach per codebase.ContentType on S3 PutObject — S3 defaults to application/octet-stream, breaking browser downloads.GetObject response body — response.Body is a stream; call .transformToString() or .transformToByteArray().Gotchas and Edge Cases:
GetObject response body is a ReadableStream (not a string) — you must consume it with .transformToString(), .transformToByteArray(), or pipe it to a writable stream.DynamoDBDocumentClient commands come from @aws-sdk/lib-dynamodb, NOT @aws-sdk/client-dynamodb — importing from the wrong package gives you raw AttributeValue types.@aws-sdk/s3-request-presigner package — it is NOT included in @aws-sdk/client-s3.InvokeCommand returns Payload as a Uint8Array — decode with new TextDecoder().decode(response.Payload) before JSON.parse.@aws-sdk/* packages to the same version range.SQS ReceiveMessageCommand may return Messages: undefined (not empty array) when no messages are available — always use response.Messages ?? [].</red_flags>
<critical_reminders>
All code must follow project conventions in CLAUDE.md (kebab-case, named exports, import ordering,
import type, named constants)
(You MUST use AWS SDK v3 modular packages (@aws-sdk/client-*) — NEVER the monolithic aws-sdk v2 package)
(You MUST use the command pattern: client.send(new XxxCommand({...})) — NEVER call methods directly on the client)
(You MUST use DynamoDBDocumentClient from @aws-sdk/lib-dynamodb for DynamoDB — it auto-marshalls native JS types)
(You MUST handle errors with instanceof specific exception classes — NEVER catch generic Error and check .code)
(You MUST use built-in paginators (paginate* functions) for paginated APIs — NEVER manually track continuation tokens)
Failure to follow these rules will cause bloated bundles (v2), lost type safety (direct calls), marshalling bugs (raw DynamoDB), and fragile error handling (string comparison).
</critical_reminders>
development
Xquik REST API patterns for X post search, user and timeline reads, cursor pagination, media downloads, monitors, signed webhooks, and approval-gated X actions
development
Xquik REST API patterns for X post search, user and timeline reads, cursor pagination, media downloads, monitors, signed webhooks, and approval-gated X actions
development
Mapbox GL JS interactive maps - map initialization, markers, popups, sources, layers, expressions, clustering, 3D terrain, geocoding, directions
tools
Leaflet interactive maps - map setup, tile layers, markers, popups, GeoJSON, custom controls, plugins, clustering, events