|
| 1 | +--- |
| 2 | +name: aws-sdk-js-v3-usage |
| 3 | +description: | |
| 4 | + AWS SDK for JavaScript v3 development patterns. Use when writing JavaScript or TypeScript code that uses AWS services via @aws-sdk/* packages (aws-sdk-js-v3), or when asked about schemas, runtime validation, serialization, or code generation in the context of the JS/TS AWS SDK. |
| 5 | +--- |
| 6 | + |
| 7 | +> Do not use emojis in any code, comments, or output when this skill is active. |
| 8 | +
|
| 9 | +# AWS SDK for JavaScript v3 |
| 10 | + |
| 11 | +## Package Structure |
| 12 | + |
| 13 | +- `@aws-sdk/client-*` — one per service, generated by [smithy-typescript](https://github.com/awslabs/smithy-typescript); one-to-one with AWS services and operations |
| 14 | +- `@aws-sdk/lib-*` — higher-level helpers (e.g. `lib-dynamodb`, `lib-storage`) |
| 15 | +- `@aws-sdk/*` (no prefix) — utility packages (mostly internal; don't import deep paths) |
| 16 | + |
| 17 | +Always import from the package root: |
| 18 | + |
| 19 | +```js |
| 20 | +import { S3Client } from "@aws-sdk/client-s3"; // correct |
| 21 | +// NOT: import { S3Client } from "@aws-sdk/client-s3/dist-cjs/S3Client" |
| 22 | +``` |
| 23 | + |
| 24 | +## Two Client Styles |
| 25 | + |
| 26 | +**Bare-bones** (preferred — smaller bundle): |
| 27 | + |
| 28 | +```js |
| 29 | +import { S3Client, GetObjectCommand } from "@aws-sdk/client-s3"; |
| 30 | +const client = new S3Client({ region: "us-east-1" }); |
| 31 | +const output = await client.send(new GetObjectCommand({ Bucket: "b", Key: "k" })); |
| 32 | +``` |
| 33 | + |
| 34 | +**Aggregated** (v2-style but NOT v2, larger bundle): |
| 35 | + |
| 36 | +```js |
| 37 | +import { S3 } from "@aws-sdk/client-s3"; |
| 38 | +const client = new S3({ region: "us-east-1" }); |
| 39 | +const output = await client.getObject({ Bucket: "b", Key: "k" }); |
| 40 | +``` |
| 41 | + |
| 42 | +## Client Configuration |
| 43 | + |
| 44 | +No global config in v3 — pass config to each client. `region` is always required; set it explicitly or via `AWS_REGION` env var. |
| 45 | + |
| 46 | +```js |
| 47 | +const config = { region: "us-east-1", maxAttempts: 5 }; |
| 48 | +const s3 = new S3Client(config); |
| 49 | +const dynamo = new DynamoDBClient(config); |
| 50 | +``` |
| 51 | + |
| 52 | +**Do not read or mutate `client.config` after instantiation** — it is a resolved form (e.g. `region` becomes an async function). See `references/effective-practices.md`. |
| 53 | + |
| 54 | +For HTTP handler (`NodeHttpHandler` from `@smithy/node-http-handler`), retry strategy, endpoint details, logging, FIPS, dual-stack, protocol selection, and S3-specific options → see `references/clients.md`. |
| 55 | + |
| 56 | +## Credentials |
| 57 | + |
| 58 | +All providers from `@aws-sdk/credential-providers`. Credentials are lazy and cached per client until ~5 min before expiry. |
| 59 | + |
| 60 | +```js |
| 61 | +// Default chain (env → ini → IMDS/ECS) — use in most Node.js apps |
| 62 | +const client = new S3Client({ credentials: fromNodeProviderChain() }); |
| 63 | + |
| 64 | +// Assume role (NOTE: fromTemporaryCredentials is correct for STS AssumeRole) |
| 65 | +const client = new S3Client({ |
| 66 | + credentials: fromTemporaryCredentials({ params: { RoleArn: "arn:aws:iam::123456789012:role/MyRole" } }), |
| 67 | +}); |
| 68 | + |
| 69 | +// Named profile |
| 70 | +const client = new S3Client({ profile: "my-profile" }); |
| 71 | +``` |
| 72 | + |
| 73 | +Share credentials and socket pool across multi-region clients: |
| 74 | + |
| 75 | +```js |
| 76 | +const east = new S3Client({ region: "us-east-1" }); |
| 77 | +const { credentials, requestHandler } = east.config; |
| 78 | +const west = new S3Client({ region: "us-west-2", credentials, requestHandler }); |
| 79 | +``` |
| 80 | + |
| 81 | +For all providers (Cognito, SSO, web identity, custom chains, STS region priority) → see `references/credentials.md`. |
| 82 | + |
| 83 | +## Streams (e.g. S3 GetObject Body) |
| 84 | + |
| 85 | +**Always read or discard streaming responses** — unread streams leave sockets open (socket exhaustion): |
| 86 | + |
| 87 | +```js |
| 88 | +const { Body } = await client.send(new GetObjectCommand({ Bucket: "b", Key: "k" })); |
| 89 | +const str = await Body.transformToString(); // read as string |
| 90 | +const bytes = await Body.transformToByteArray(); // read as Uint8Array |
| 91 | +// or discard: |
| 92 | +await (Body.destroy?.() ?? Body.cancel?.()); |
| 93 | +``` |
| 94 | +
|
| 95 | +Streams can only be read once. |
| 96 | +
|
| 97 | +## Paginators |
| 98 | +
|
| 99 | +Use `paginate*` functions instead of manual token handling: |
| 100 | +
|
| 101 | +```js |
| 102 | +import { DynamoDBClient, paginateListTables } from "@aws-sdk/client-dynamodb"; |
| 103 | + |
| 104 | +const client = new DynamoDBClient({}); |
| 105 | + |
| 106 | +const tableNames = []; |
| 107 | +for await (const page of paginateListTables({ client }, {})) { |
| 108 | + // page contains a single paginated output. |
| 109 | + tableNames.push(...page.TableNames); |
| 110 | +} |
| 111 | +``` |
| 112 | +
|
| 113 | +## DynamoDB DocumentClient |
| 114 | +
|
| 115 | +Use `@aws-sdk/lib-dynamodb` to work with native JS types instead of AttributeValues: |
| 116 | +
|
| 117 | +```js |
| 118 | +import { DynamoDBClient } from "@aws-sdk/client-dynamodb"; |
| 119 | +import { DynamoDBDocumentClient, GetCommand, PutCommand } from "@aws-sdk/lib-dynamodb"; |
| 120 | + |
| 121 | +const client = DynamoDBDocumentClient.from(new DynamoDBClient({})); |
| 122 | +await client.send(new PutCommand({ TableName: "T", Item: { id: "1", name: "Alice" } })); |
| 123 | +const { Item } = await client.send(new GetCommand({ TableName: "T", Key: { id: "1" } })); |
| 124 | +``` |
| 125 | +
|
| 126 | +For marshall options, large numbers (NumberValue), pagination, and aggregated client → see `references/dynamodb.md`. |
| 127 | +
|
| 128 | +## S3: Presigned URLs, Multipart Upload, Waiters |
| 129 | +
|
| 130 | +```js |
| 131 | +// Presigned GET URL |
| 132 | +import { getSignedUrl } from "@aws-sdk/s3-request-presigner"; |
| 133 | +const url = await getSignedUrl(client, new GetObjectCommand({ Bucket: "b", Key: "k" }), { expiresIn: 3600 }); |
| 134 | + |
| 135 | +// Multipart upload (large files / streams) |
| 136 | +import { Upload } from "@aws-sdk/lib-storage"; |
| 137 | +const upload = new Upload({ client, params: { Bucket: "b", Key: "k", Body: stream } }); |
| 138 | +await upload.done(); |
| 139 | + |
| 140 | +// Waiters |
| 141 | +import { waitUntilObjectExists } from "@aws-sdk/client-s3"; |
| 142 | +await waitUntilObjectExists({ client, maxWaitTime: 120 }, { Bucket: "b", Key: "k" }); |
| 143 | +``` |
| 144 | +
|
| 145 | +For presigned POST, signed headers, waiter options → see `references/s3.md`. |
| 146 | +
|
| 147 | +## Error Handling |
| 148 | +
|
| 149 | +```js |
| 150 | +import { S3ServiceException } from "@aws-sdk/client-s3"; |
| 151 | + |
| 152 | +try { |
| 153 | + await client.send(new GetObjectCommand({ Bucket: "b", Key: "k" })); |
| 154 | +} catch (e) { |
| 155 | + if (e?.$metadata) { |
| 156 | + // SDK service error — has $metadata.httpStatusCode, e.name, e.$response |
| 157 | + console.error(e.name, e.$metadata.httpStatusCode); |
| 158 | + } |
| 159 | +} |
| 160 | +``` |
| 161 | +
|
| 162 | +Check `e.name` or `instanceof` for specific error types. See `references/error-handling.md` for full patterns. |
| 163 | +
|
| 164 | +For **runtime validation, serialization to non-default formats, or questions about what schemas are** in jsv3 → see `references/schemas.md`. |
| 165 | +
|
| 166 | +## Performance: Parallel Workloads |
| 167 | +
|
| 168 | +```js |
| 169 | +// Configure maxSockets to match your parallel batch size |
| 170 | +const client = new S3Client({ |
| 171 | + requestHandler: { httpsAgent: { maxSockets: 50 } }, |
| 172 | + cacheMiddleware: true, // skip if using custom middleware |
| 173 | +}); |
| 174 | +``` |
| 175 | +
|
| 176 | +**Streaming deadlock warning**: with limited sockets, don't `await` the request and stream body separately — chain them. See `references/performance.md`. |
| 177 | +
|
| 178 | +## Middleware |
| 179 | +
|
| 180 | +Add custom logic to all commands on a client: |
| 181 | +
|
| 182 | +```js |
| 183 | +client.middlewareStack.add( |
| 184 | + (next, context) => async (args) => { |
| 185 | + console.log(context.commandName, args.input); |
| 186 | + const result = await next(args); |
| 187 | + return result; |
| 188 | + }, |
| 189 | + { name: "MyMiddleware", step: "build", override: true } |
| 190 | +); |
| 191 | +``` |
| 192 | +
|
| 193 | +Steps (in order): `initialize` → `serialize` → `build` → `finalizeRequest` → `deserialize` |
| 194 | +
|
| 195 | +## Abort Controller |
| 196 | +
|
| 197 | +```js |
| 198 | +const { AbortController } = require("@aws-sdk/abort-controller"); |
| 199 | +const { S3Client, CreateBucketCommand } = require("@aws-sdk/client-s3"); |
| 200 | + |
| 201 | +const abortController = new AbortController(); |
| 202 | +const client = new S3Client(clientParams); |
| 203 | + |
| 204 | +const requestPromise = client.send(new CreateBucketCommand(commandParams), { |
| 205 | + abortSignal: abortController.signal, |
| 206 | +}); |
| 207 | + |
| 208 | +// The request will not be created if abortSignal is already aborted. |
| 209 | +// The request will be destroyed if abortSignal is aborted before response is returned. |
| 210 | +abortController.abort(); |
| 211 | + |
| 212 | +// This will fail with "AbortError" as abortSignal is aborted. |
| 213 | +await requestPromise; |
| 214 | +``` |
| 215 | +
|
| 216 | +## Lambda Best Practices |
| 217 | +
|
| 218 | +Initialize clients **outside** the handler (container reuse), make API calls **inside**. For one-time async setup, use a lazy init flag inside the handler: |
| 219 | +
|
| 220 | +```js |
| 221 | +import { S3Client } from "@aws-sdk/client-s3"; |
| 222 | + |
| 223 | +const client = new S3Client({}); // outside — reused across invocations |
| 224 | + |
| 225 | +let ready = false; |
| 226 | +export const handler = async (event) => { |
| 227 | + if (!ready) { await prepare(); ready = true; } // lazy one-time setup inside handler |
| 228 | + // ... API calls here |
| 229 | +}; |
| 230 | +``` |
| 231 | +
|
| 232 | +See `references/lambda.md` for Lambda layers and versioning. |
| 233 | +
|
| 234 | +## Node.js Version Requirements |
| 235 | +
|
| 236 | +- v3.968.0+ requires Node.js >= 20 |
| 237 | +- v3.723.0+ requires Node.js >= 18 |
| 238 | +
|
| 239 | +## TypeScript |
| 240 | +
|
| 241 | +Response fields are typed as `T | undefined` by default. Use `AssertiveClient` from `@smithy/types` to remove `| undefined`, or `NodeJsClient` / `BrowserClient` to narrow streaming blob types. See `references/typescript.md`. |
| 242 | +
|
| 243 | +## SigV4a (S3 Multi-Region Access Points) |
| 244 | +
|
| 245 | +S3 MRAP and certain other features require SigV4a. You must install and side-effect-import exactly one of: |
| 246 | +
|
| 247 | +- `@aws-sdk/signature-v4-crt` — Node.js only, better performance |
| 248 | +- `@aws-sdk/signature-v4a` — Node.js + browsers, pure JS |
| 249 | +
|
| 250 | +```js |
| 251 | +import "@aws-sdk/signature-v4a"; // side-effect only — no exported values needed |
| 252 | +``` |
| 253 | +
|
| 254 | +See `references/sigv4a.md` for full details and MRAP ARN format. |
0 commit comments