Encodera

Thanks for reading!

Donate to the site — we work hard for you.

Contents

Child processes

A child process is another program that yours starts: git, an archiver, an image converter or another Node.js script. The node:child_process module lets you start it, pass arguments and get the result. This is useful when an existing utility already does what you need.

execFile: getting the result

execFile() starts a program, waits for it to finish and returns all its output. Combined with promisify() from node:util it can be awaited:

import { execFile } from 'node:child_process';
import { promisify } from 'node:util';
 
const run = promisify(execFile);
 
const { stdout } = await run(process.execPath, ['--version']);
 
console.log(`Node.js version: ${stdout.trim()}`);

process.execPath is the full path to the node program. The example runs it with --version and prints, for example, "Node.js version: v22.12.0". stdout is what the program printed.

Running your own script

Another script is started the same way, with arguments:

import { execFile } from 'node:child_process';
import { writeFile } from 'node:fs/promises';
import { promisify } from 'node:util';
 
const run = promisify(execFile);
 
await writeFile('count.mjs', 'console.log(process.argv.slice(2).length);');
const { stdout } = await run(process.execPath, ['count.mjs', 'a', 'b', 'c']);
 
console.log(`Arguments: ${stdout.trim()}`); // Arguments: 3

The example first creates a tiny script count.mjs, then runs it as a separate program.

An error in the child process

If the program exits with a code other than 0, the promise rejects. The error holds the exit code and everything the program printed:

import { execFile } from 'node:child_process';
import { promisify } from 'node:util';
 
const run = promisify(execFile);
 
try {
  await run(process.execPath, ['-e', 'process.exit(3)']);
} catch (error) {
  console.log(`The program exited with code ${error.code}`); // 3
}

spawn: long commands

spawn() does not collect output but passes it on as it appears. The option stdio: 'inherit' prints everything straight to your terminal — handy for builds or package installs:

// scripts/backup.js
import { spawn } from 'node:child_process';
 
const child = spawn('git', ['log', '--oneline', '-5'], { stdio: 'inherit' });
 
child.on('close', (code) => {
  console.log(`git exited with code ${code}`);
});

node scripts/backup.js shows the project's last five commits and then the exit code.

FunctionWhen to use
execFile()Short output needed in full
spawn()Long work, large output
exec()A shell string with && or redirection is needed

Common mistake

exec() runs a string through the command shell. If you insert user data into it, the user can append a command of their own:

// src/server.js
// Dangerous: exec(`convert ${fileName} out.png`)
// fileName = 'a.jpg; rm -rf ~' runs a second command

Use execFile() or spawn() with an array of arguments: each argument is passed to the program as is and is not parsed by the shell.

Thanks for reading!

Donate to the site — we work hard for you.