1 · Structure and rendering · 30 MIN
Files, paths and resource boundaries
File access should use a deliberate base path and explicit encoding.
The filesystem promises API provides asynchronous operations. A file URL resolved against import.meta.url locates a resource relative to the module rather than whichever directory launched the process. This example writes only inside a new temporary directory and cleans it up in finally. A real endpoint must not pass an arbitrary user path directly to readFile: input paths can escape the intended directory. readFile buffers the entire file, so use streaming for large inputs.
Run from a different working directory.
Read the example
import {mkdtemp,writeFile,readFile,rm} from 'node:fs/promises';
import {tmpdir} from 'node:os';
import {join} from 'node:path';
const dir=await mkdtemp(join(tmpdir(),'codingneed-lesson-'));
try {
const file=join(dir,'notes.txt');
await writeFile(file,'Learn paths safely.','utf8');
console.log(await readFile(file,'utf8'));
} finally {await rm(dir,{recursive:true,force:true})}
Check the expected output
Learn paths safely. The newly created temporary directory is removed afterwards.
Your challenge
Add a missing-file case and handle only ENOENT as an expected absence; preserve other errors.
Solution cost: O(b) file I/O for b bytes. time · O(b) readFile buffering. space
Common trap
Catching every file error as not found hides permission and disk failures.
Study the project implementation
import {mkdtemp,writeFile,readFile,rm} from 'node:fs/promises';
import {tmpdir} from 'node:os';
import {join} from 'node:path';
const dir=await mkdtemp(join(tmpdir(),'codingneed-lesson-'));
try {
const file=join(dir,'notes.txt');
await writeFile(file,'Learn paths safely.','utf8');
console.log(await readFile(file,'utf8'));
} finally {await rm(dir,{recursive:true,force:true})}
Further reading: Official documentation
Next lesson: Streams and backpressure →