The AWS SDK v3

The AWS SDK v3 ships one package per service: you build a client, send a command, and let the credential chain find your keys.

Syntaxconst out = await client.send(new SomeCommand(input), { abortSignal });

The AWS SDK for JavaScript v3 is the official library for calling AWS services from Node.js. A service is something like S3 (file storage) or SQS (message queues). Each service has its own npm package, so you install only the ones you call: npm install @aws-sdk/client-s3.

Client, command, send

Every call has the same three parts. A client holds the settings shared by all calls. A command is one operation plus its input. client.send(command) returns a promise.

import { S3Client, PutObjectCommand } from "@aws-sdk/client-s3";
const s3 = new S3Client({ region: "eu-west-1" });
const out = await s3.send(new PutObjectCommand({
  Bucket: "reports", Key: "q1.txt", Body: "hello",
}));
console.log(out.$metadata.httpStatusCode);

A region is the group of data centres your resources live in, written as eu-west-1 or us-east-1. Pass it, or set the AWS_REGION environment variable.

Credentials

Credentials are the keys that prove which account you are. You rarely pass them. The default chain tries sources in order and stops at the first that answers: environment variables (AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY, AWS_SESSION_TOKEN), IAM Identity Center, the shared files under ~/.aws/, a web identity token file (how a Kubernetes pod gets credentials), ECS container credentials, then EC2 instance metadata.

Shorthand and longhand

Two spellings of the same timeout config. Longhand builds the handler object yourself; the shorthand, added in SDK v3.521.0, passes its options directly and the SDK builds it for you.

// longhand
import { NodeHttpHandler } from "@smithy/node-http-handler";
new S3Client({ requestHandler: new NodeHttpHandler({ requestTimeout: 3000 }) });

// shorthand, same result
new S3Client({ requestHandler: { requestTimeout: 3000 } });

Example

Try it yourself
Loading editor…
Press Run to execute the code.

When to use it

  • Write a file to S3 from a web handler without checking in any access keys.
  • Give a container running in Kubernetes credentials through its service account instead of environment variables.
  • Cut a cold-start or image size by installing one service package rather than a whole SDK.

More examples

Command pattern and the aggregated client

Both lines call S3 GetObject and return the same response. Reach for the command form in anything you deploy, because unused commands can be removed at build time; reach for the aggregated client in a script or a REPL session where size does not matter. Recognising both matters more than choosing: you will read code written in each.

Example · javascript
// Longhand: the command pattern. Bundlers can drop the commands you never
// import, which keeps a deployment package small.
import { S3Client, GetObjectCommand } from "@aws-sdk/client-s3";
const client = new S3Client({});
const a = await client.send(new GetObjectCommand({ Bucket: "reports", Key: "q1.txt" }));

// Shorthand: the aggregated client. Same operation, one method call. It pulls
// in every command for the service, so nothing can be dropped.
import { S3 } from "@aws-sdk/client-s3";
const s3 = new S3({});
const b = await s3.getObject({ Bucket: "reports", Key: "q1.txt" });

A deadline on one call

Client-level timeouts apply to every call. This applies a 2 second deadline to one call only, which is what you want when a single request sits inside an HTTP handler that has its own budget. Use it for the calls a user is waiting on, and leave background work on the client default.

Example · javascript
import { S3Client, GetObjectCommand } from "@aws-sdk/client-s3";

const s3 = new S3Client({ region: process.env.AWS_REGION });

// send() takes a second argument of per-call options. abortSignal cancels
// this one call without touching the client's own timeouts.
// AbortSignal.timeout() was added in Node.js 17.3.0.
try {
  const out = await s3.send(
    new GetObjectCommand({ Bucket: "reports", Key: "q1.txt" }),
    { abortSignal: AbortSignal.timeout(2000) }
  );
  console.log(await out.Body.transformToString());
} catch (err) {
  // An aborted request rejects rather than hanging.
  console.error("get failed:", err.name);
}

Reading config from the environment

One module owns the client and the config, and every other file imports it. Creating the client once means its connection pool and its credential cache are shared, instead of each request paying to resolve credentials again. The explicit checks turn a missing variable into a clear startup error.

Example · javascript
// index.mjs
import { SQSClient } from "@aws-sdk/client-sqs";

// Nothing about credentials appears here. In development the shared files
// under ~/.aws/ answer; in a container, the role attached to the workload does.
// Fail fast on the values you do need, so a misconfigured deploy dies at
// startup rather than on the first request.
const { AWS_REGION, ORDERS_QUEUE_URL } = process.env;
if (!AWS_REGION) throw new Error("AWS_REGION is not set");
if (!ORDERS_QUEUE_URL) throw new Error("ORDERS_QUEUE_URL is not set");

export const sqs = new SQSClient({ region: AWS_REGION });
export const queueUrl = ORDERS_QUEUE_URL;

Discussion

  • Be the first to comment on this lesson.