Skip to main content
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. 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.

TypeScript: typing command inputs and outputs

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);
    }
  }
};

Build docs developers (and LLMs) love