A command is a typed request object that represents a single AWS API operation. Each operation in a service has its own command class (for example, GetObjectCommand, ListTablesCommand). You construct a command with the operation’s input parameters, then pass it to a client’s .send() method.
This separation between client and command is what allows bundlers to tree-shake unused operations from your bundle.
Basic usage
const { DynamoDBClient, ListTablesCommand } = require("@aws-sdk/client-dynamodb");
const client = new DynamoDBClient({ region: "us-west-2" });
const command = new ListTablesCommand({});
const result = await client.send(command);
console.log(result.TableNames.join("\n"));
Every client.send() call returns a promise. Use await or .then() to handle the result.
Importing commands
Commands are exported from the same service package as the client:
import { S3Client, GetObjectCommand, PutObjectCommand } from "@aws-sdk/client-s3";
import { DynamoDBClient, ListTablesCommand, GetItemCommand } from "@aws-sdk/client-dynamodb";
Each import is a separate, independently tree-shakeable chunk. Only the commands you import end up in your bundle.
Bare-bones vs aggregated
There are two ways to invoke an operation.
Bare-bones (recommended)
Import S3Client and each command separately. The client exposes only .send(). Bundlers can eliminate any command you do not import.
import { S3Client } from "@aws-sdk/client-s3";
import { GetObjectCommand } from "@aws-sdk/client-s3";
const client = new S3Client({});
await client.send(new GetObjectCommand({ Bucket: "my-bucket", Key: "my-key" }));
Aggregated (v2-style)
Import S3 (without the Client suffix). All operations are methods on the class. This is equivalent to the v2 SDK style, but imports every command and is not tree-shakeable.
import { S3 } from "@aws-sdk/client-s3";
const client = new S3({});
await client.getObject({ Bucket: "my-bucket", Key: "my-key" });
Using the aggregated client increases bundle size because all commands for that service are included, even those you never call. Prefer the bare-bones client in browser applications and AWS Lambda functions.
Every command class is generic. Use the input and output types exported from the service package for full type safety.
import {
S3Client,
GetObjectCommand,
GetObjectCommandInput,
GetObjectCommandOutput,
} from "@aws-sdk/client-s3";
const input: GetObjectCommandInput = {
Bucket: "my-bucket",
Key: "my-key",
};
const client = new S3Client({ region: "us-east-1" });
const command = new GetObjectCommand(input);
const output: GetObjectCommandOutput = await client.send(command);
console.log(output.$metadata.httpStatusCode);
The $metadata field is present on every command output and contains the HTTP status code, request ID, and other transport-level details.
Handling errors
Wrap client.send() in a try/catch. The SDK throws typed service exceptions that include the HTTP status code and a machine-readable error code.
import { S3Client, GetObjectCommand } from "@aws-sdk/client-s3";
import { S3ServiceException } from "@aws-sdk/client-s3";
const client = new S3Client({ region: "us-east-1" });
try {
const result = await client.send(
new GetObjectCommand({ Bucket: "my-bucket", Key: "missing-key" })
);
} catch (err) {
if (err instanceof S3ServiceException) {
console.error("S3 error:", err.name, err.$metadata.httpStatusCode);
} else {
throw err;
}
}
Cancellation with AbortSignal
Pass an abortSignal as the second argument to client.send() to cancel an in-flight request. The promise rejects with an AbortError when the signal fires.
const { S3Client, CreateBucketCommand } = require("@aws-sdk/client-s3");
const controller = new AbortController();
const client = new S3Client({});
const requestPromise = client.send(
new CreateBucketCommand({ Bucket: "my-bucket" }),
{ abortSignal: controller.signal }
);
// Cancel the request at any point before it completes
controller.abort();
try {
await requestPromise;
} catch (err) {
if (err.name === "AbortError") {
console.log("Request was cancelled");
}
}
The request is not created if the signal is already aborted before .send() is called. If the signal fires while the request is in-flight, the underlying connection is destroyed.
A practical browser example—cancelling an S3 upload when the user clicks an abort button:
const abortController = new AbortController();
const abortSignal = abortController.signal;
const uploadBtn = document.querySelector(".upload");
const abortBtn = document.querySelector(".abort");
uploadBtn.addEventListener("click", uploadObject);
abortBtn.addEventListener("click", () => {
abortController.abort();
console.log("Upload aborted");
});
const uploadObject = async (file) => {
const client = new S3Client(clientParams);
try {
await client.send(new PutObjectCommand(commandParams), { abortSignal });
} catch (e) {
if (e.name === "AbortError") {
console.log("Upload aborted:", e.message);
}
}
};