Node.js Child Process
Spawn and manage child processes with exec, spawn, fork, and IPC communication
When to Use
- Running external programs or shell commands from Node.js
- Executing system tools (ffmpeg, imagemagick, git, curl)
- Running Node.js scripts as separate processes with IPC
- Parallelizing work across CPU cores with full process isolation
Instructions
execfor simple commands with buffered output:
import { exec } from 'node:child_process';
import { promisify } from 'node:util';
const execAsync = promisify(exec);
const { stdout, stderr } = await execAsync('git log --oneline -5');
console.log(stdout);
execFilefor running executables (no shell, safer):
import { execFile } from 'node:child_process';
import { promisify } from 'node:util';
const execFileAsync = promisify(execFile);
const { stdout } = await execFileAsync('node', ['--version']);
spawnfor streaming output from long-running processes:
import { spawn } from 'node:child_process';
const child = spawn('ffmpeg', ['-i', 'input.mp4', '-c:v', 'libx264', 'output.mp4']);
child.stdout.on('data', (data) => console.log(`stdout: ${data}`));
child.stderr.on('data', (data) => console.error(`stderr: ${data}`));
child.on('close', (code) => {
console.log(`Process exited with code ${code}`);
});
forkfor Node.js worker scripts with IPC:
// parent.ts
import { fork } from 'node:child_process';
const child = fork('./worker.ts');
child.send({ task: 'process', data: [1, 2, 3] });
child.on('message', (result) => {
console.log('Result from child:', result);
});
// worker.ts
process.on('message', (msg: any) => {
const result = msg.data.map((n: number) => n * 2);
process.send!(result);
});
- Handle errors and exit codes:
import { spawn } from 'node:child_process';
function run(command: string, args: string[]): Promise<string> {
return new Promise((resolve, reject) => {
const child = spawn(command, args);
let stdout = '';
let stderr = '';
child.stdout.on('data', (data) => {
stdout += data;
});
child.stderr.on('data', (data) => {
stderr += data;
});
child.on('error', reject); // Spawn error (command not found)
child.on('close', (code) => {
if (code === 0) resolve(stdout);
else reject(new Error(`Exit code ${code}: ${stderr}`));
});
});
}
- Set environment and working directory:
const child = spawn('npm', ['run', 'build'], {
cwd: '/path/to/project',
env: { ...process.env, NODE_ENV: 'production' },
timeout: 60_000,
});
- Kill processes on cleanup:
const child = spawn('long-running-process');
process.on('SIGTERM', () => {
child.kill('SIGTERM');
});
// Or with AbortController
const ac = new AbortController();
const child = spawn('command', [], { signal: ac.signal });
ac.abort(); // Kills the child process
Details
exec runs a shell command, buffers the entire output, and returns it. spawn streams output as it arrives. fork creates a Node.js child process with a built-in IPC channel.
exec vs spawn vs fork:
exec— shell, buffered output, 1MB default limit. Best for short commandsspawn— no shell (by default), streaming output, no buffer limit. Best for long-running or large-output processesfork— specializedspawnfor Node.js scripts with IPC. Best for parallel Node.js work
Security: exec runs through a shell, making it vulnerable to command injection. Never pass user input to exec without sanitization. Use execFile or spawn (no shell) for user-provided arguments.
Trade-offs:
execis simple — but buffers all output in memory and uses a shellspawnstreams output — but requires manual output collectionforkhas built-in IPC — but only works with Node.js scripts- Child processes have full isolation — but higher overhead than worker threads
Source
https://nodejs.org/api/child_process.html
Process
- Read the instructions and examples in this document.
- Apply the patterns to your implementation, adapting to your specific context.
- Verify your implementation against the details and edge cases listed above.
Harness Integration
- Type: knowledge — this skill is a reference document, not a procedural workflow.
- No tools or state — consumed as context by other skills and agents.
Success Criteria
- The patterns described in this document are applied correctly in the implementation.
- Edge cases and anti-patterns listed in this document are avoided.