Child Processes (exec & spawn)
The child_process module lets a Node program launch and control other programs — running shell commands, external tools, or another Node script — and read their output, errors, and exit codes.
Learn Child Processes (exec & spawn) in our free Node.js course — a beginner-friendly interactive lesson with worked examples, a practice exercise and a…
Part of the free Node.js course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.
By the end of this lesson you'll know when to reach for exec, execFile, spawn, or fork, how to capture streaming versus buffered output, how to pass arguments safely, and how to read stdout, stderr, and exit codes from the program you started.
What You'll Learn in This Lesson
1️⃣ exec & execFile — Buffered Output
exec runs a command through a shell and collects all of its output into memory, then calls your callback with (err, stdout, stderr) . That's perfect for short commands with small output. Because it uses a shell, features like pipes and quotes work — but it also means untrusted input can be dangerous.
execFile is the safer cousin: it runs an executable directly and takes its arguments as an array, so there's no shell to misread special characters. Prefer it whenever any argument could come from user input.
2️⃣ spawn — Streaming Output
When output is large or arrives over time, buffering it all in memory is wasteful and can hit a maxBuffer limit. spawn instead gives you streams : child.stdout and child.stderr emit 'data' events as chunks arrive, and a 'close' event fires with the exit code when the program ends.
Note that chunks can arrive in any grouping — three lines might come as one chunk or three — so we collect them and print once on 'close' to keep the output predictable.
3️⃣ fork — Node Talking to Node
fork is a special case of spawn built for launching another Node script . Beyond starting the process, it opens an automatic IPC channel , so the parent and child can pass structured messages with child.send(...) and process.on('message', ...) .
This is how you offload work to a separate process while still coordinating. The child below sums an array it's sent, replies with the result, then exits. You need two files — the child script and the parent.
Your turn. The program below works once you fill in the two blanks marked ___ . Follow the 👉 hints, then run it and compare with the expected output.
Mini-Challenge: A Tiny Compute Server
No blanks this time — just a brief and an outline. Build the two files, run them, and check your output against the example in the comments. This ties together fork , send , and the message/exit events.
Common Errors (and the fix)
- ❌ "Error: spawn ENOENT" — Node can't find the program you named. ✅ Check the command exists and is on your PATH, or use its full path.
- ❌ "stdout maxBuffer length exceeded" — exec buffered too much output. ✅ Switch to spawn and stream, or raise { maxBuffer } .
- ❌ Command injection from user input — You built an exec string with untrusted data. ✅ Use execFile / spawn with an args array — no shell.
- ❌ Child's error was ignored — You read stdout but never checked the exit code. ✅ Handle the 'close' event's code and read stderr .
- ❌ fork() messages never arrive — You forked a non-Node file or forgot process.on('message') in the child. ✅ fork is for Node scripts; wire up both send and on('message') .
Pro Tips
- 💡 Promisify exec with const { promisify } = require('node:util') so you can await it and use try/catch.
- 💡 Prefer arguments arrays (execFile/spawn) over building shell strings — safer and no quoting headaches across operating systems.
- 💡 Use child_process for non-Node tools (git, ffmpeg) and reach for worker_threads when you need fast in-process CPU work — that's the next lesson.
📋 Quick Reference — child_process
Function
Best for
exec(cmd, cb)
short shell commands, buffered
execFile(file, args, cb)
run a binary safely, no shell
spawn(cmd, args)
large/streaming output
fork(modulePath)
another Node script + IPC
Frequently Asked Questions
🎉 Lesson Complete!
- ✅ exec runs a shell command and buffers all output for you
- ✅ execFile skips the shell and takes args as an array — faster and safer
- ✅ spawn streams output via 'data' events for large or long output
- ✅ fork launches another Node script with a built-in IPC channel
- ✅ Always handle stderr and the exit code — children can fail quietly
- ✅ Next lesson: Worker Threads & Cluster — fast CPU work and scaling across cores
Practice quiz
How does exec deliver a command's output?
- It streams output chunk by chunk
- It writes directly to a file
- It buffers all output in memory and hands it to a callback
- It returns a Promise of a stream
Answer: It buffers all output in memory and hands it to a callback. exec runs the command in a shell and buffers stdout/stderr into memory, good for short commands with small output.
When should you prefer spawn over exec?
- When output is large or long-running, so it can be streamed
- When the command is a quick one-liner
- When you need shell pipes
- Never; exec is always better
Answer: When output is large or long-running, so it can be streamed. spawn streams output as events instead of buffering, avoiding 'maxBuffer exceeded' for large output.
What is the key difference between exec and execFile?
- execFile always uses a shell
- exec cannot capture output
- execFile only runs Node scripts
- execFile skips the shell and passes arguments as an array
Answer: execFile skips the shell and passes arguments as an array. execFile runs the binary directly with an args array, making it faster and safer against command injection.
Why is execFile safer than exec with untrusted input?
- It encrypts the arguments
- No shell means special characters cannot become commands
- It runs as a different user
- It validates the input automatically
Answer: No shell means special characters cannot become commands. Because execFile skips the shell, there is no shell to misinterpret special characters as command injection.
How does spawn deliver streaming output?
- Through child.stdout and child.stderr 'data' events
- Through a single return value
- By writing to process.env
- Only via the 'message' event
Answer: Through child.stdout and child.stderr 'data' events. spawn does not buffer; you read chunks via child.stdout.on('data', ...) and child.stderr.on('data', ...).
What does fork add on top of spawning a process?
- Automatic shell access
- A built-in HTTP server
- An automatic IPC channel for message passing
- Shared memory with the parent
Answer: An automatic IPC channel for message passing. fork launches another Node script and sets up an IPC channel, so parent and child exchange messages.
Which methods does fork use for parent-child messaging?
- child.write(...) and process.read(...)
- child.send(...) and process.on('message', ...)
- exec(...) and eval(...)
- child.emit(...) and process.listen(...)
Answer: child.send(...) and process.on('message', ...). The parent calls child.send(msg) and the child receives it with process.on('message', ...), and can reply with process.send.
When is fork the right choice over spawn?
- When running git or ffmpeg
- When you need a shell pipeline
- When output must be buffered
- When launching another Node.js script you want to message
Answer: When launching another Node.js script you want to message. fork is for launching another Node script with an IPC channel; use spawn or exec for non-Node programs.
What does an exit code of 0 from a child process mean?
- A crash
- Success
- Still running
- Permission denied
Answer: Success. An exit code of 0 means the child finished successfully; non-zero indicates an error.
Which event's callback gives you a child's numeric exit code?
- 'data'
- 'spawn'
- 'exit' or 'close'
- 'ready'
Answer: 'exit' or 'close'. Listen for the 'exit' or 'close' event whose callback receives the exit code.
Continue this course
- Previous: Timers & the Event Loop Phases
- Next: Worker Threads & Cluster