javascript

AWS SDK for JavaScript v3

Node.js, Bun and Deno — the modular S3 client

Install

shell
npm install @aws-sdk/client-s3 @aws-sdk/lib-storage @aws-sdk/s3-request-presigner

Configure

Configure the endpoint, region, credential and bucket in this tool’s vocabulary. Path-style addressing is recommended for dotted bucket names and clients without nested wildcard TLS, but virtual-hosted addressing is also supported.

In AWS SDK for JavaScript v3Set it to
endpointhttps://s3.canada.popcloud.ca
regioncanada
credentials.accessKeyIdPCAK00EXAMPLEKEYID00
credentials.secretAccessKey<your secret access key>
forcePathStyletrue
Environment
example values
export AWS_ACCESS_KEY_ID=PCAK00EXAMPLEKEYID00
export AWS_SECRET_ACCESS_KEY=<your secret access key>
export AWS_ENDPOINT_URL=https://s3.canada.popcloud.ca
export AWS_REGION=canada
export POPCLOUD_BUCKET=pc-your-org-media

These are example values. Sign in and every snippet on this site fills in with your own endpoint, key and bucket.

JavaScript
import { S3Client } from "@aws-sdk/client-s3";

const s3 = new S3Client({
  endpoint: process.env.AWS_ENDPOINT_URL, // https://s3.<region>.popcloud.ca
  region: process.env.AWS_REGION,         // canada
  credentials: {
    accessKeyId: process.env.AWS_ACCESS_KEY_ID,
    secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
  },
  // Recommended for predictable custom-endpoint behavior. Virtual-hosted
  // requests are supported too for ordinary single-label bucket names.
  forcePathStyle: true,
});

Use it

Upload an object

JavaScript
await s3.send(
  new PutObjectCommand({
    Bucket: bucket,
    Key: key,
    Body: body,
    ContentType: "text/plain",
  }),
);

Upload a large object

JavaScript
// Handles multipart, concurrency and retries for you — use it for anything
// whose size you do not control.
const upload = new Upload({
  client: s3,
  params: { Bucket: bucket, Key: key, Body: body },
  queueSize: 4,
  partSize: 8 * 1024 * 1024,
});
upload.on("httpUploadProgress", (p) => console.log(`${p.loaded}/${p.total ?? "?"}`));
await upload.done();

Download an object

JavaScript
const object = await s3.send(new GetObjectCommand({ Bucket: bucket, Key: key }));
const text = await object.Body.transformToString();

List a prefix

JavaScript
for await (const page of paginateListObjectsV2(
  { client: s3 },
  { Bucket: bucket, Prefix: prefix },
)) {
  for (const object of page.Contents ?? []) {
    keys.push(object.Key);
  }
}

Presign a URL

JavaScript
const url = await getSignedUrl(
  s3,
  new GetObjectCommand({ Bucket: bucket, Key: key }),
  { expiresIn: 3600 }, // seconds; 7 days is the hard maximum
);
// Anyone holding this URL can read the object until it expires, with no
// credentials at all.

Delete an object

JavaScript
await s3.send(new DeleteObjectCommand({ Bucket: bucket, Key: key }));

What to watch for

These apply to AWS SDK for JavaScript v3 specifically. The full list covers the platform.

You’ll see
A GET carrying If-None-Match, If-Match or If-Modified-Since returns 400 InvalidRequest. Expected behaviour is 304 Not Modified for a cache hit and 412 PreconditionFailed for a stale precondition.
Why
The conditional headers are not handled correctly on the read path. This is a defect, not a design decision.
Instead
Do not send conditional headers on GET. If you are putting a CDN in front of a bucket, cache on a fixed TTL rather than on revalidation, and version your object keys so a new key means new content. Conditional *writes* (If-None-Match on PUT) are separately unsupported.
You’ll see
405 MethodNotAllowed from CreateBucket or DeleteBucket — `aws s3 mb`, `mc mb`, or a tool provisioning its own bucket on first run.
Why
Bucket lifecycle belongs to the control plane, which also allocates the storage account, the data-plane key and the CORS rules that come with it. Letting the edge create buckets would create half of one.
Instead
Create buckets in the dashboard, or with `POST /v1/buckets` on the control-plane API. Everything object-level then works normally against that bucket.

How this page is kept true

Every snippet above was extracted from examples/node/aws-sdk-js-v3, a program that uploads, downloads, compares bytes, lists, presigns and cleans up after itself. It runs in CI against a live sandbox organisation via make docs-verify-node. If it stops passing, this page is wrong and we treat that as a bug in the product.

A few snippets on this page are marked not run in CI — they need a browser, a second provider, or a configuration change the sandbox cannot make. Those are reviewed by hand.

Not yet run against the live sandbox — the runner is wired, the first verified run stamps this line.