AWS S3
Node.js
AWS SDK
cloud storage
data migration

How to copy/move all objects in Amazon S3 from one prefix to other using the AWS SDK for Node.js

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

In Amazon S3, a prefix is just the beginning of an object key, not a real folder. That means copying or moving "all files under a folder" actually means listing objects with one prefix and recreating them under another. In Node.js, the AWS SDK handles this well, but you need to account for pagination, key rewriting, and the difference between copy and move.

The Basic Strategy

To copy every object from one prefix to another, the usual flow is:

  1. List all objects with the source prefix.
  2. Build a destination key for each object.
  3. Copy each object.
  4. If you want a move, delete the original only after a successful copy.

That last step matters because S3 has no native rename operation for prefixes.

Copying Objects With AWS SDK for Node.js

This example uses AWS SDK v3 and paginates through all matching keys.

javascript
1import {
2  S3Client,
3  CopyObjectCommand,
4  DeleteObjectCommand,
5  paginateListObjectsV2,
6} from "@aws-sdk/client-s3";
7
8const s3 = new S3Client({ region: "us-east-1" });
9
10async function copyOrMovePrefix({
11  bucket,
12  fromPrefix,
13  toPrefix,
14  removeSource = false,
15}) {
16  const paginator = paginateListObjectsV2(
17    { client: s3 },
18    { Bucket: bucket, Prefix: fromPrefix }
19  );
20
21  for await (const page of paginator) {
22    for (const obj of page.Contents ?? []) {
23      const fromKey = obj.Key;
24      const toKey = fromKey.replace(fromPrefix, toPrefix);
25
26      await s3.send(
27        new CopyObjectCommand({
28          Bucket: bucket,
29          Key: toKey,
30          CopySource: encodeURI(`${bucket}/${fromKey}`),
31        })
32      );
33
34      if (removeSource) {
35        await s3.send(
36          new DeleteObjectCommand({
37            Bucket: bucket,
38            Key: fromKey,
39          })
40        );
41      }
42    }
43  }
44}

The important detail is encodeURI on CopySource, because spaces and special characters in keys can otherwise break the request.

Copy Versus Move

A copy leaves the original objects in place. A move is really copy-plus-delete.

Use the function like this:

javascript
1await copyOrMovePrefix({
2  bucket: "my-app-bucket",
3  fromPrefix: "incoming/2026/",
4  toPrefix: "archive/2026/",
5  removeSource: false,
6});

For a move:

javascript
1await copyOrMovePrefix({
2  bucket: "my-app-bucket",
3  fromPrefix: "incoming/2026/",
4  toPrefix: "archive/2026/",
5  removeSource: true,
6});

In operational code, copy first, verify, then delete. Never delete optimistically.

Prefix Rewriting Rules

The key rewrite should be deliberate. If fromPrefix is too broad, replace() may change more than intended. In most jobs, you should ensure:

  • 'fromPrefix ends with / when treating it like a directory'
  • destination prefix does not overlap source unintentionally
  • you test a few sample keys before running a large job

For example, moving from logs/ to logs-archive/ is safe. Moving from log to archive is too vague and can produce unexpected keys.

Verifying the Result

After the copy or move, verify with the CLI or with another SDK listing pass:

bash
aws s3 ls s3://my-app-bucket/incoming/2026/ --recursive
aws s3 ls s3://my-app-bucket/archive/2026/ --recursive

Verification matters because a partial run may still leave some objects in the old prefix. For larger migrations, record counts before and after the move.

Performance and Safety Considerations

For very large prefixes, copying sequentially may be slow. You can add controlled concurrency later, but do not start there. A correct sequential implementation is easier to reason about and recover if something fails.

If metadata, storage class, encryption, or ACL behavior matters, review those fields explicitly in CopyObjectCommand. A prefix migration is often simple until one of those settings suddenly matters.

Common Pitfalls

  • Treating S3 prefixes as real folders and looking for a rename operation that does not exist.
  • Forgetting to paginate listings and therefore copying only the first page of objects.
  • Deleting the source object before confirming the copy succeeded.
  • Rewriting keys with a prefix pattern that is broader than intended.
  • Ignoring special characters in CopySource and getting copy failures on some keys.

Summary

  • Moving an S3 prefix is really list, copy, and optionally delete.
  • Use paginated listing so every matching object is processed.
  • Rewrite destination keys carefully and verify a few examples first.
  • For moves, delete only after successful copy.
  • Start with a correct sequential implementation before adding concurrency.

Course illustration
Course illustration

All Rights Reserved.