Listing & pagination
List the keys in a Lockwell bucket with prefix, delimiter, and common-prefix semantics, plus full pagination loops on the native and S3 clients.
Listing walks the keys in a bucket. This page covers both clients, the delimiter and common-prefix model, and full paging loops.
- The native client has
listObjectswith prefix/delimiter semantics and an iterator that follows continuation tokens. - The S3 client has two forms:
ListObjectsV2(the token-paged default) andListObjects(the older marker-paged v1), plus paginators that follow the tokens for you.
For the operation matrix, see the native data-plane reference and the S3 operations reference.
One page
A single listing call returns one page. prefix filters by key prefix; maxKeys caps the page (the server caps it at
1000); the page reports isTruncated and a nextContinuationToken when more remains.
const page = await native.listObjects("reports", { prefix: "q1/", maxKeys: 100 });
for (const o of page.objects) console.log(o.key, o.size);
console.log(page.isTruncated, page.nextContinuationToken);The S3 ListObjectsV2 returns the same page shape:
const page = await s3.listObjectsV2("reports", { prefix: "q1/", maxKeys: 100 });
for (const o of page.objects) console.log(o.key, o.size, o.etag);
console.log(page.isTruncated, page.nextContinuationToken);Listing options
| Option | Native (Go / Node / Java) | S3 (Go / Node / Java) |
|---|---|---|
| Key prefix | Prefix / prefix / ListObjectsOptions.prefix | WithPrefix / prefix / arg 2 |
| Delimiter | Delimiter / delimiter / ListObjectsOptions.delimiter | WithDelimiter / delimiter / v1 form |
| Start after a key | StartAfter (Go) / startAfter / startAfter on the wire | WithStartAfter / startAfter |
| Continuation token | ContinuationToken / continuationToken / arg 3 | WithContinuationToken / continuationToken / arg 4 |
| Max keys per page | MaxKeys / maxKeys / ListObjectsOptions.maxKeys | WithMaxKeys / maxKeys / arg 3 |
startAfter begins the listing after a given key (a one-shot starting point, not a resume token). continuationToken
resumes a previously truncated listing.
Pass `startAfter` or `continuationToken`, not both. They serve different purposes: one sets a starting key,
the other resumes a paged listing.
Delimiters and common prefixes
A delimiter (almost always "/") makes the listing behave like a directory walk. Keys that share a prefix up to the
next delimiter are collapsed into a single entry in commonPrefixes; only keys with no further delimiter past the
prefix appear in objects.
Given these keys:
logs/2026/01/a.log
logs/2026/01/b.log
logs/2026/02/c.log
logs/index.txta listing with prefix: "logs/" and delimiter: "/" returns:
objects:logs/index.txtcommonPrefixes:logs/2026/
To descend, list again with the common prefix as the new prefix. Without a delimiter, the listing is flat: every key
under the prefix comes back in objects.
const page = await native.listObjects("archive", { prefix: "logs/", delimiter: "/" });
console.log(page.commonPrefixes); // ["logs/2026/"]
for (const o of page.objects) console.log(o.key); // "logs/index.txt"const page = await s3.listObjectsV2("archive", { prefix: "logs/", delimiter: "/" });
console.log(page.commonPrefixes); // ["logs/2026/"]
for (const o of page.objects) console.log(o.key); // "logs/index.txt"Paging across all keys
To walk every key, follow isTruncated with the continuation token until it clears. The native client ships an iterator
that does this for you; the S3 client ships paginators per listing operation.
The native iterator (ListObjectsAll in Go, the paginateObjects async generator in Node) transparently follows
continuation tokens.
for await (const page of native.paginateObjects("reports", { prefix: "q1/" })) {
for (const o of page.objects) console.log(o.key, o.size);
}The S3 client ships an auto-pager for each listing operation, so you never thread a continuation token by hand. The loop shape is the same in every language: ask whether more pages remain, fetch the next page, repeat.
// Node uses async iterators rather than a paginator object:
for await (const page of s3.paginateObjectsV2("reports", { prefix: "q1/" })) {
for (const o of page.objects) console.log(o.key, o.size);
}The Go paginators (NewListObjectsV2Paginator, NewListObjectVersionsPaginator, NewListMultipartUploadsPaginator,
NewListPartsPaginator) own the tokens. Seed them with the same options the one-shot method takes, but do not pass a
continuation/marker yourself: the paginator manages it.
Paging by hand
If you would rather drive the loop yourself, follow isTruncated with the continuation token until it clears.
let token;
do {
const page = await native.listObjects(
"reports",
token ? { prefix: "q1/", continuationToken: token } : { prefix: "q1/" },
);
for (const o of page.objects) console.log(o.key);
token = page.isTruncated ? page.nextContinuationToken : undefined;
} while (token);let token;
do {
const page = await s3.listObjectsV2(
"reports",
token ? { prefix: "q1/", continuationToken: token } : { prefix: "q1/" },
);
for (const o of page.objects) console.log(o.key);
token = page.isTruncated ? page.nextContinuationToken : undefined;
} while (token);ListObjects v1 (marker pagination)
The S3 client also exposes the v1 ListObjects, which pages by a marker (the last key returned) rather than an opaque
token. Prefer ListObjectsV2 (or the native listing) for new code; v1 exists for parity with clients that still page by
marker.
let marker;
do {
const page = await s3.listObjects("reports", marker ? { prefix: "v1/", marker } : { prefix: "v1/" });
for (const o of page.objects) console.log(o.key);
marker = page.isTruncated ? page.nextMarker || page.objects.at(-1)?.key : undefined;
} while (marker);When a delimiter collapses the truncation boundary onto a common prefix, the server may omit NextMarker. In that case
resume from the last key or common prefix you saw, which is what the Node and Go paginators do for you.
Listing versions and multipart uploads
Versioned listings (ListObjectVersions) and in-progress multipart uploads (ListMultipartUploads, ListParts) share
this prefix/delimiter/marker model. They each have a dedicated page:
- Versioning: list versions and delete markers.
- Multipart uploads: list uploads and their parts.
Next steps
- Upload & download: the put/get/head you are listing.
- Copying objects: duplicate keys you found in a listing.
- Deleting objects: batch-delete a listed prefix.
Installation
Install Lockwell's Go, Node, Java, Native Wire, SolidStart, and Spring integrations, then configure credentials, retries, TLS, and runtime boundaries.
Migration and compatibility
Migrate an S3 application to Lockwell without overstating compatibility, and understand native SDK language and feature boundaries.