child_process
Running shell commands and spawning processes from Node.js — exec, spawn, execFile, fork, and handling output.
exec() collects a command's entire output in memory and gives up past maxBuffer, which is 1 MB by default. A command that prints more than that, such as a long git log, a database dump or a verbose build, is killed and the call fails with ERR_CHILD_PROCESS_STDIO_MAXBUFFER. It works in testing with small output, then fails in production with real data.
import { exec } from "child_process";
import { promisify } from "util";
const execAsync = promisify(exec);
// a command with a lot of output -- here 2 MB, like a big `git log` or a database dump
try {
const { stdout } = await execAsync(`node -e "process.stdout.write('x'.repeat(2_000_000))"`);
console.log("got", stdout.length, "bytes");
} catch (e) {
console.log(e.code, "|", e.message);
}
// ERR_CHILD_PROCESS_STDIO_MAXBUFFER | stdout maxBuffer length exceededRunning commands from Node.js
exec — run a shell command
import { exec } from "child_process";
import { promisify } from "util";
const execAsync = promisify(exec);
// Run a git command:
const { stdout, stderr } = await execAsync("git log --oneline -5");
console.log(stdout); // last 5 commits
// Check Node.js version:
const { stdout: version } = await execAsync("node --version");
console.log(version.trim()); // "v20.15.0"
// Handle errors:
try {
await execAsync("ls /nonexistent");
} catch (e) {
console.error("Command failed:", e.message);
console.error("Exit code:", e.code); // e.g. 2
console.error("Stderr:", e.stderr);
}Use exec() for commands with small, predictable output, like the version check above. For anything that can grow, use spawn() (next section), which streams the output in chunks with no size limit. If you really need it all as one string, you can raise the limit with exec(cmd, { maxBuffer: 50 * 1024 * 1024 }), but that just moves the cliff.
import { spawn } from "child_process";
// spawn streams the output instead of collecting it into one buffer:
const child = spawn("node", ["-e", "process.stdout.write('x'.repeat(2_000_000))"]);
let bytes = 0;
child.stdout.on("data", (chunk) => { bytes += chunk.length; }); // process as it arrives
child.on("close", (code) => console.log("exit", code, "| got", bytes, "bytes"));
// exit 0 | got 2000000 bytesspawn — streaming output for long commands
import { spawn } from "child_process";
// Good for: npm install, build tools, long-running processes
const install = spawn("npm", ["install"], {
cwd: "./my-project", // working directory
stdio: "pipe", // capture stdout/stderr (default)
});
install.stdout.on("data", (chunk) => process.stdout.write(chunk));
install.stderr.on("data", (chunk) => process.stderr.write(chunk));
install.on("close", (code) => {
if (code === 0) console.log("Install successful!");
else console.error(`Install failed with code ${code}`);
});
// Kill a process:
const proc = spawn("long-running-command");
setTimeout(() => proc.kill("SIGTERM"), 10000); // kill after 10sexec() — it goes through the shell.
Use execFile() or spawn() with an args array instead.
Official Sources
- Node.js Docs — child_process
- Node.js Docs — child_process.exec()
- Node.js Docs — child_process.spawn()
- execa — npm package
The Codex links only to official documentation and the ECMAScript specification. No third-party tutorials.