Files and paths
How to use the Node.js fs module with async/await
Learn how to use the Node.js fs module with async/await through the promise-based node:fs/promises API, so you can await calls like fs.readdir() directly.
Node’s filesystem module has a stable promise API in node:fs/promises:
import { readFile, readdir } from 'node:fs/promises'
const posts = await readdir('content')
const configText = await readFile('config.json', 'utf8')
const config = JSON.parse(configText)
The node: prefix makes it explicit that this is a Node built-in rather than a package from node_modules. readFile() returns a Buffer unless you request an encoding such as 'utf8'.
An awaited filesystem failure rejects the promise. Catch errors where you can add useful context or make a specific recovery decision:
async function loadOptionalConfig(path) {
try {
return JSON.parse(await readFile(path, 'utf8'))
} catch (error) {
if (error.code === 'ENOENT') return {}
throw new Error(`Cannot load ${path}`, { cause: error })
}
}
Do not turn every error into an empty value. ENOENT means the path is missing; invalid JSON, permission errors, and I/O failures should remain visible.
Avoid checking with access() before reading. The file can change between the check and the read. Attempt the real operation and handle its result.
Promise-based filesystem work uses Node’s worker pool, so it does not block the event-loop thread while waiting for ordinary file I/O. It is not automatically safe to launch overlapping writes to the same file. Those operations are not synchronized and can overwrite or corrupt the intended result.
Use new URL('./config.json', import.meta.url) when a resource belongs beside an ES module. Use a path relative to process.cwd() when the caller deliberately selects a file from the working directory.
Wrap the entry point so a rejected promise does not become an unhandled rejection:
main().catch((error) => {
console.error(error)
process.exitCode = 1
})
Try this on your own: load one valid JSON file, one missing file, and one malformed file. Recover only from ENOENT, preserve the original error as cause for the others, and explain why a preflight existence check would still race. Print the parsed object for the valid file so you know the read succeeded.
Lesson completed