search
Find objects whose key matches a glob (default), regex, substring, or exact pattern - a streaming async iterable that walks every page like listAll.
files.search(pattern, options?)
Find objects whose key matches pattern, walking every page like listAll. It’s a streaming async iterable of StoredFile, so it stays memory-bounded on large buckets and you can break to stop early.
// Glob is the default: `*` stays within a path segment, `?` matches one char.
for await (const file of files.search("avatars/*.png")) {
console.log(file.key, file.size);
}
// Collect into an array when you want them all at once:
const pdfs = await Array.fromAsync(files.search("invoices/2024/*.pdf"));
Matching is against the caller-facing key, so a client prefix on the instance is already stripped before the pattern is tested.
Glob syntax
"glob" mode uses standard glob semantics, powered by picomatch:
*— any run of characters within a path segment (does not cross/).**— a globstar segment that spans path segments. Write it as its own segment:photos/**/*.jpgmatches at any depth, including zero subfolders.?— a single non-/character.[a-z],{a,b}— character classes and brace alternation.!pattern— negation (matches everything except).
The pattern is anchored to the whole key, and dotfiles are matched (object keys are opaque, not hidden files). A glob with no wildcards is an exact match, not a substring — files.search("report.pdf") matches the key report.pdf and nothing else. Use match: "substring" for “contains”.
// Every JPEG at any depth under photos/:
for await (const file of files.search("photos/**/*.jpg")) {
// photos/cover.jpg, photos/2024/spain/beach.jpg, ...
}
Match modes
Pass a match mode to change how a string pattern is interpreted, or pass a RegExp directly (which always matches by regex and ignores match):
// Regular expression (string form):
files.search("\\.(png|jpe?g)$", { match: "regex" });
// ...or a RegExp instance:
files.search(/\.(png|jpe?g)$/);
// Substring — key contains the text anywhere:
files.search("report", { match: "substring" });
// Exact — key equals the text:
files.search("invoices/2024/q1.pdf", { match: "exact" });
// Case-insensitive (any mode):
files.search("*.PNG", { caseInsensitive: true });
An invalid regex pattern throws a FilesError before the walk begins.
Scoping the walk with prefix
search reads every page under a prefix, following the cursor. For a glob, the literal head of the pattern is pushed down automatically as that prefix, so files.search("uploads/2024/*.pdf") scopes the walk to the uploads/2024 prefix rather than the whole bucket.
Other modes carry no inferable prefix, so for a regex, substring, or caseInsensitive search over a large bucket, pass prefix yourself to bound the walk:
// Only walk logs/ — then regex-match within it:
files.search("error|panic", { match: "regex", prefix: "logs/" });
A glob’s auto push-down is disabled when caseInsensitive is set (a provider’s prefix filter is case-sensitive), so combine caseInsensitive with an explicit prefix to scope it.
Stopping early
maxResults caps the number of matches yielded; because the walk is lazy, it also stops paging once the cap is hit. Equivalently, break out of the loop.
// First 10 matches, then stop fetching pages:
const recent = await Array.fromAsync(
files.search("**/*.log", { maxResults: 10 })
);
Provider support
search runs on every adapter, since it’s built on listAll — there’s no per-provider search capability and nothing to gate. The two listAll caveats carry over: on Netlify Blobs omit limit to walk everything, and the non-recursive Box / OneDrive / SharePoint adapters only see the immediate children of the root folder. An unbounded search with no prefix walks the whole bucket by design.
Options
SearchOptions: Cannot read properties of undefined (reading 'ESNext')The match mode is one of:
SearchMatch: Cannot read properties of undefined (reading 'ESNext')