Node.js Child Process — Complete Guide to Spawning and Managing Subprocesses
In this tutorial, you will learn about Node.js Child Process. We cover key concepts, practical examples, and best practices to help you master this topic.
The Node.js child_process module enables executing system commands and spawning subprocesses, allowing your application to interact with the operating system and other programs.
What You'll Learn
By the end of this tutorial, you'll use spawn, exec, execFile, and fork to run external programs, capture output, pipe data, and communicate between processes.
Why Child Processes Matter
Server applications often need to run shell commands, execute Python scripts, process images with external tools, or leverage multi-core CPUs. Child processes provide this capability.
Real-World Use
A file processing API receives uploaded images, spawns ImageMagick to resize them, captures the processing output, and returns the result to the user.
Child Process Learning Path
flowchart LR
A[Events] --> B[Child Process]
B --> C[Cluster]
C --> D[Worker Threads]
D --> E[Express.js]
A --> F{You Are Here}
style F fill:#f90,color:#fff
spawn — Streaming Output
spawn launches a command with streaming I/O, suitable for large output.
import { spawn } from "node:child_process";
const ls = spawn("ls", ["-lh", "/home"]);
ls.stdout.on("data", (data) => {
process.stdout.write(data);
});
ls.stderr.on("data", (data) => {
console.error(data.toString());
});
ls.on("close", (code) => {
console.log(`Process exited with code ${code}`);
});
exec — Buffered Output
exec runs a command in a shell and buffers the output. Good for short commands with small output.
import { exec } from "node:child_process";
exec("df -h | grep /dev/sda", (error, stdout, stderr) => {
if (error) {
console.error(`Error: ${error.message}`);
return;
}
console.log(stdout);
});
execFile — No Shell
execFile runs a binary directly without a shell, making it more secure.
import { execFile } from "node:child_process";
execFile("python3", ["--version"], (error, stdout) => {
if (error) throw error;
console.log(stdout); // Python 3.x.x
});
fork — Node.js Child Process
fork spawns a new Node.js process with IPC (inter-process communication) channel.
// parent.js
import { fork } from "node:child_process";
const child = fork("./child.js");
child.send({ cmd: "compute", data: [1, 2, 3, 4, 5] });
child.on("message", (result) => {
console.log("Result from child:", result);
});
// child.js
process.on("message", (msg) => {
if (msg.cmd === "compute") {
const sum = msg.data.reduce((a, b) => a + b, 0);
process.send({ result: sum });
}
});
Pipe Between Processes
import { spawn } from "node:child_process";
const find = spawn("find", [".", "-type", "f", "-name", "*.js"]);
const wc = spawn("wc", ["-l"]);
find.stdout.pipe(wc.stdin);
wc.stdout.on("data", (data) => {
console.log(`Total JS files: ${data}`); // Total JS files: 42
});
Handling Errors
import { spawn } from "node:child_process";
const child = spawn("nonexistent-command");
child.on("error", (err) => {
console.error("Failed to spawn:", err.message);
});
child.on("exit", (code, signal) => {
if (code !== 0) {
console.error(`Exited with code ${code}, signal ${signal}`);
}
});
Common Mistakes
1. Using exec With Untrusted Input
exec uses a shell, making it vulnerable to Command Injection. Use execFile or spawn with untrusted input.
2. Not Handling stdout/stderr Buffers
Buffered output from exec is limited by maxBuffer (default 1024KB). Use spawn for large output.
3. Forgetting to Handle Process Exit
Long-running child processes may exit unexpectedly. Always listen for 'exit' and 'error' events.
4. Zombie Processes
Not waiting for child process cleanup creates zombies. Listen for 'close' events or use 'detached' + 'unref'.
5. Assuming Synchronous Execution
Child processes are asynchronous. Use async/await with promises or callbacks for sequential logic.
Practice Questions
1. What is the difference between spawn and exec?
spawn streams output and uses no shell. exec buffers output and uses a shell. Use spawn for large data, exec for short commands.
2. When should you use fork instead of spawn?
Use fork to spawn another Node.js process with an IPC channel for message passing between parent and child.
3. How do you prevent command injection with child_process?
Use spawn or execFile instead of exec. They don't use a shell, so arguments are passed safely to the command.
4. What is maxBuffer and what happens when it's exceeded?
maxBuffer (default 1024KB) limits stdout/stderr data in exec. Exceeding it kills the process and returns an error.
5. Challenge: Write a script that runs a Python script, captures its output, and logs execution time.
import { spawn } from "node:child_process";
const start = Date.now();
const python = spawn("python3", ["-c", "import time; time.sleep(1); print('Done')"]);
python.stdout.on("data", (data) => {
console.log(data.toString());
});
python.on("close", () => {
const elapsed = ((Date.now() - start) / 1000).toFixed(2);
console.log(`Completed in ${elapsed}s`);
});
FAQ
Mini Project: File Watcher with Notification
Build a file watcher that monitors a directory and runs a command when files change.
import fs from "node:fs";
import { spawn } from "node:child_process";
const watchDir = process.argv[2] || "./watch";
const command = process.argv.slice(3).join(" ") || "echo File changed";
fs.watch(watchDir, (event, filename) => {
console.log(`${filename} changed, running command...`);
const child = spawn("sh", ["-c", command], { stdio: "inherit" });
child.on("close", (code) => console.log(`Command exited with ${code}`));
});
What's Next
Node.js Cluster Node.js Worker Threads Express.js
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro