Runtime APIs
Use Node.js packages with clear boundaries
Run Node.js APIs and npm packages in Bun while identifying the compatibility assumptions your application depends on.
8 minute lesson
Bun implements many Node.js built-in modules. Use the explicit node: prefix when importing one:
import { join } from 'node:path'
const file = join('data', 'notes.json')
console.log(file)
This code runs in Bun and Node.js. The prefix also tells the reader that path comes from the runtime, not node_modules.
Most npm packages install normally:
bun add zod
Then import them with standard ES module syntax:
import { z } from 'zod'
const Note = z.object({
title: z.string().min(1),
})
console.log(Note.parse({ title: 'Learn Bun' }))
Compatibility needs evidence
A successful installation only proves the package was downloaded. Run the paths your application uses.
Pay extra attention when a dependency uses:
- a native Node.js add-on
- a recently added Node.js API
- Node.js internals rather than public APIs
- process, stream, or module behavior at the edges
Check Bun’s Node.js compatibility documentation when something fails. Then create a small reproduction before changing the application.
You can detect Bun at runtime with:
if (process.versions.bun) {
console.log(`Running Bun ${process.versions.bun}`)
}
Use runtime checks only when behavior must differ. Shared Web APIs such as Request, Response, fetch, and URL usually create a cleaner boundary than many Bun-versus-Node branches.
Lesson completed