Sometimes you need to run an entirely separate program, a shell command, an image conversion tool, another script, from inside a Node.js application. This lesson covers the child_process module for exactly that.
Running External Programs From Node.js
The child_process module lets Node.js spawn and communicate with separate operating system processes, running shell commands, external binaries, or even other Node.js scripts entirely outside the current process's memory space and event loop.
It offers a few different functions depending on your needs: exec() (buffers output, good for short commands), spawn() (streams output, good for long-running or large-output processes), and fork() (spawns another Node.js process specifically, with a built-in message-passing channel).
exec() buffers the entire output in memory before calling the callback, fine for short commands, but risky for commands that produce large amounts of output.
exec vs spawn vs fork
exec(command, callback) // buffered output, shell command string
spawn(command, args, options) // streamed output, array of arguments
fork(modulePath, args, options) // spawns another Node.js process
exec() runs a full command string through a shell, convenient but riskier with untrusted input.
spawn() takes the command and arguments separately, streaming output as it's produced.
fork() is specifically for launching another Node.js script, with built-in IPC (inter-process communication).
All three return an object you can listen to for 'exit', 'error', and (for spawn/fork) stream events.
child_process Cheatsheet
Choosing the right function for the task.
Function
Best For
exec()
Short shell commands with small output
spawn()
Long-running processes or large/streamed output
execFile()
Running a specific executable directly, without a shell
fork()
Running another Node.js script with message passing
Using spawn() for Streamed Output
spawn() returns an object whose stdout/stderr are actual readable streams, letting you process output incrementally, essential for long-running commands or ones that produce more output than you'd want buffered entirely in memory.
exec() runs its command through a shell, if any part of that command string comes from user input without careful sanitization, an attacker could inject additional shell commands. spawn()/execFile() with arguments passed as a separate array avoid this risk, since the arguments are never interpreted by a shell.
// Risky: user input concatenated into a shell command
exec(`convert ${userFileName} output.png`); // never do this
// Safer: arguments passed separately, no shell interpretation
spawn('convert', [userFileName, 'output.png']);
Common Mistakes
Using exec() with unsanitized user input concatenated into the command string, risking command injection.
Using exec() for commands that produce large output, risking excessive memory usage from buffering.
Forgetting to handle the 'error' event on a spawned or forked process.
Using fork() when a simpler in-process function call (or a Worker Thread) would suffice.
Key Takeaways
child_process lets Node.js run external commands, binaries, or other Node.js scripts as separate OS processes.
exec() is convenient for short commands; spawn() streams output for long-running or large-output processes.
fork() is specialized for launching another Node.js process with built-in message passing.
Never interpolate untrusted input into an exec() command string, use spawn()/execFile() with array arguments instead.
Pro Tip
Default to spawn() with arguments as a separate array rather than exec() with a single command string, even for commands that seem safe today. It avoids shell interpretation entirely and closes off command injection risk as a habit, not an afterthought.
You now know how to safely run external processes. Next, learn clustering, using multiple processes to take advantage of multi-core CPUs.