fsPromises.glob()
Streams the paths matching one or more glob patterns as an async iterator.
fsPromises.glob(pattern[, options])- Added in Node.js v22.2.0
Parameters
| Parameter | Type | Description |
|---|---|---|
$pattern | string | string[] | A glob pattern, or an array of patterns whose matches are combined. |
$optionsoptional | Object | Glob options. |
$options.cwdoptional | string= process.cwd() | Directory the pattern is resolved against, and the base for the yielded relative paths. |
$options.excludeoptional | Function= undefined | Filter called per candidate; return true to drop it. Later releases also accept a glob pattern or array of patterns here. |
$options.withFileTypesoptional | boolean= false | Yield fs.Dirent objects instead of path strings. |
Return value
AsyncIterator<string | fs.Dirent>Returns an AsyncIterator that yields each matching path as a string, or an fs.Dirent when withFileTypes is true.
Errors
Iteration rejects with ENOENT if cwd does not exist. Unreadable directories encountered during the walk are skipped rather than throwing.
Changelog
- PHP v22.2.0Added in v22.2.0, initially as an experimental API — expect a stability warning on older 22.x releases.
- PHP v22.xThe exclude option was later widened from a filter function to also accept glob pattern strings; the function form works across all versions that have glob.
fsPromises.glob() walks the filesystem and yields paths matching a shell-style pattern — * for one segment, ** for any depth, ? for one character, {a,b} for alternatives. It removes the need for a third-party glob package for ordinary matching.
It is an iterator, not a promise
This is the detail that catches everyone. glob() returns an AsyncIterator, so await fs.glob('**/*.js') does not give you an array — it gives you the iterator object back. Consume it with for await (const p of fs.glob(pattern)), or collect it with Array.fromAsync().
Streaming is the point: results arrive as the walk finds them, so you can start work on the first match before the tree has been fully traversed, and a huge tree never builds a huge array.
What trips people up
- Paths are yielded relative to
cwd, so with acwdoption they are not usable from the process's own directory without joining. - Directories match too. Filter with
withFileTypes: trueandisFile()when you only want files. - Match order is filesystem order — sort if you need stable output.
Example
When to use it
- Collect every test file under a project without a glob dependency.
- Find all Markdown files to feed a static site build.
- Process log files matching a date pattern one at a time.
- Locate stray artifacts to delete during a clean step.
Frequently asked questions
Why does await fsPromises.glob() not return an array?
Because glob() returns an AsyncIterator rather than a promise. Use for await (const p of fs.glob(pattern)) or await Array.fromAsync(fs.glob(pattern)).
Do I still need the glob npm package in Node 22?
Not for ordinary patterns — fsPromises.glob covers *, **, ?, character classes and brace expansion. The package still offers more options, such as absolute paths, custom sorting and richer ignore handling.
Does fsPromises.glob return absolute paths?
No, it yields paths relative to cwd (which defaults to process.cwd()). Join them with path.resolve() if you need absolute paths.
More examples
Collect matches into an array
Prints the two .js paths, then all three. Array.fromAsync() drains the iterator into an array in one expression; passing an array of patterns unions their matches without walking the tree twice.
import fs from 'node:fs/promises';
await fs.mkdir('src/lib', { recursive: true });
await fs.writeFile('src/index.js', '');
await fs.writeFile('src/lib/util.js', '');
await fs.writeFile('src/notes.txt', '');
const matches = await Array.fromAsync(fs.glob('src/**/*.js'));
console.log(matches.sort());
// Multiple patterns in one pass.
const mixed = await Array.fromAsync(fs.glob(['src/**/*.js', 'src/**/*.txt']));
console.log(mixed.sort());
await fs.rm('src', { recursive: true });Files only, with a cwd
The first list includes the posts and drafts directories; the second has only the two .md names. It also shows that cwd makes the yielded paths relative to 'site', not to the process directory.
import fs from 'node:fs/promises';
await fs.mkdir('site/posts/drafts', { recursive: true });
await fs.writeFile('site/posts/one.md', '');
await fs.writeFile('site/posts/drafts/two.md', '');
const all = await Array.fromAsync(fs.glob('**', { cwd: 'site' }));
console.log('everything (dirs included):', all.sort());
const files = [];
for await (const d of fs.glob('**', { cwd: 'site', withFileTypes: true })) {
if (d.isFile()) files.push(d.name);
}
console.log('files only:', files.sort());
await fs.rm('site', { recursive: true });Excluding a subtree
Prints unfiltered: 2 and filtered: [ 'app/main.js' ]. The exclude callback runs during the walk, so skipping a directory prunes it instead of matching everything and filtering afterwards.
import fs from 'node:fs/promises';
await fs.mkdir('app/node_modules/dep', { recursive: true });
await fs.writeFile('app/main.js', '');
await fs.writeFile('app/node_modules/dep/index.js', '');
const withVendor = await Array.fromAsync(fs.glob('app/**/*.js'));
console.log('unfiltered:', withVendor.length);
const mine = await Array.fromAsync(
fs.glob('app/**/*.js', { exclude: (p) => String(p).includes('node_modules') })
);
console.log('filtered:', mine);
await fs.rm('app', { recursive: true });
Discussion