Home > Blog > Desenvolvimento Web
Desenvolvimento Web
JavaScript
Programação

child_process no Node.js

Atualizado em: 28 de setembro de 2026

Terminal Linux usado em desenvolvimento e administração de aplicações Node.js

O módulo node:child_process permite iniciar programas externos e outros processos Node.js. Ele é usado para executar ferramentas de linha de comando, integrar binários, separar tarefas, criar workers baseados em processo e automatizar operações do sistema.

Processos filhos possuem memória isolada e falham separadamente da aplicação principal. Em contrapartida, consomem mais recursos que Worker Threads e exigem cuidado especial com argumentos, sinais, streams e segurança.

Quatro APIs principais

  • spawn: inicia um comando e trabalha com streams;
  • exec: executa por meio de shell e acumula a saída;
  • execFile: executa um arquivo diretamente e acumula a saída;
  • fork: inicia outro processo Node.js com canal de IPC.

A escolha correta evita vulnerabilidades e problemas de memória.

spawn para saída contínua

import { spawn } from 'node:child_process';

const child = spawn('node', ['--version'], {
  stdio: ['ignore', 'pipe', 'pipe'],
});

child.stdout.setEncoding('utf8');
child.stdout.on('data', (chunk) => {
  process.stdout.write(chunk);
});

child.stderr.setEncoding('utf8');
child.stderr.on('data', (chunk) => {
  process.stderr.write(chunk);
});

child.on('error', (error) => {
  console.error('Falha ao iniciar', error);
});

child.on('close', (code, signal) => {
  console.log({ code, signal });
});

spawn é apropriado para saída grande, processos longos e pipelines, pois não precisa armazenar todo o conteúdo em memória.

exec e o risco do shell

import { exec } from 'node:child_process';

exec('node --version', (error, stdout, stderr) => {
  if (error) {
    console.error(error);
    return;
  }
  console.log(stdout);
});

exec interpreta a string em um shell. Nunca concatene entrada do usuário:

// Inseguro
exec(`convert ${userFilename} output.png`);

Caracteres especiais podem executar comandos adicionais. Prefira spawn ou execFile com array de argumentos.

execFile

import { execFile } from 'node:child_process';

execFile('node', ['--version'], (error, stdout) => {
  if (error) throw error;
  console.log(stdout);
});

Como o arquivo é executado diretamente por padrão, há menos exposição a expansão de shell. Ainda assim, valide caminhos, permissões e argumentos aceitos pelo programa.

Promisificando execFile

import { execFile } from 'node:child_process';
import { promisify } from 'node:util';

const execFileAsync = promisify(execFile);

const { stdout, stderr } = await execFileAsync('node', ['--version'], {
  timeout: 5000,
  maxBuffer: 1024 * 1024,
});

maxBuffer limita a saída acumulada. Para volumes imprevisíveis, use spawn.

fork e IPC

fork é especializado em processos Node.js e cria um canal para mensagens:

import { fork } from 'node:child_process';

const child = fork(new URL('./worker.js', import.meta.url), [], {
  stdio: ['ignore', 'inherit', 'inherit', 'ipc'],
});

child.on('message', (message) => {
  console.log('Resposta', message);
});

child.send({ type: 'calculate', payload: 100 });

No filho:

process.on('message', (message) => {
  if (message.type === 'calculate') {
    process.send?.({ result: message.payload * 2 });
  }
});

Mensagens são serializadas. Não envie objetos enormes nem dependa de referências compartilhadas.

Tratando eventos

Eventos importantes:

  • spawn: processo iniciado;
  • error: falha ao iniciar ou enviar sinal;
  • exit: processo terminou;
  • close: streams foram fechados;
  • disconnect: canal IPC fechado;
  • message: mensagem recebida.

Trate error e close. Um comando inexistente pode emitir erro sem o fluxo esperado de saída.

Exit code

Código zero normalmente indica sucesso, mas o contrato depende do programa. Registre código, sinal, duração e uma parte limitada do stderr.

function run(command, args) {
  return new Promise((resolve, reject) => {
    const child = spawn(command, args);
    let stderr = '';

    child.stderr.on('data', (chunk) => {
      if (stderr.length < 16_000) stderr += chunk;
    });

    child.on('error', reject);
    child.on('close', (code, signal) => {
      if (code === 0) resolve();
      else reject(new Error(`Falha code=${code} signal=${signal}: ${stderr}`));
    });
  });
}

Timeout e AbortSignal

const controller = new AbortController();
const timer = setTimeout(() => controller.abort(), 10_000);

try {
  const child = spawn('programa', ['--modo', 'batch'], {
    signal: controller.signal,
  });
  await waitForClose(child);
} finally {
  clearTimeout(timer);
}

Confirme como o programa reage ao sinal. Alguns processos criam descendentes que continuam executando.

Sinais e encerramento

child.kill('SIGTERM');

Enviar um sinal não garante encerramento imediato. Aplique um prazo e, se necessário, escalone para uma finalização mais forte. No Windows, sinais e grupos de processos possuem diferenças.

Processos descendentes

Quando um comando inicia outros processos, encerrar apenas o pai pode deixar órfãos. Evite shell:true sem necessidade, entenda a árvore criada e use ferramentas específicas para gerenciamento de grupos quando a aplicação for multiplataforma.

stdio

Opções comuns:

  • pipe: cria stream;
  • inherit: compartilha o terminal;
  • ignore: descarta;
  • ipc: canal de mensagens.
spawn('npm', ['test'], {
  stdio: 'inherit',
});

Em servidores, herdar stdout pode gerar logs sem estrutura. Pipe permite limitar, transformar e correlacionar.

Backpressure

Se você escreve no stdin do filho, respeite o retorno de write:

if (!child.stdin.write(chunk)) {
  await new Promise((resolve) => child.stdin.once('drain', resolve));
}
child.stdin.end();

Ignorar backpressure pode aumentar memória.

Pipeline

import { pipeline } from 'node:stream/promises';

const gzip = spawn('gzip', ['-c']);
await pipeline(inputStream, gzip.stdin);
await pipeline(gzip.stdout, outputStream);

Trate também o exit code do processo. Um pipeline concluído não garante que o binário tenha finalizado com sucesso.

Ambiente e diretório

spawn('node', ['script.js'], {
  cwd: '/app/jobs',
  env: {
    ...process.env,
    NODE_ENV: 'production',
    JOB_ID: jobId,
  },
});

Não passe segredos desnecessários. Variáveis podem ficar visíveis a ferramentas do sistema e dumps. Use um ambiente mínimo quando possível.

PATH e executáveis

Ambientes de produção, containers e serviços systemd podem ter PATH diferente do terminal. Use imagem previsível, valide dependências no startup ou configure caminhos absolutos controlados.

Shell true

shell:true é útil para sintaxe de shell, mas amplia riscos de injeção e diferenças entre plataformas. Evite quando puder representar o comando e os argumentos diretamente.

Pool de processos

Para tarefas frequentes, criar processos repetidamente custa memória e startup. Mantenha um pequeno pool com fork ou use uma fila externa. Implemente limite de tarefas, reinício após falha e reciclagem para controlar vazamentos.

Observabilidade

Registre:

  • nome permitido do comando;
  • duração;
  • exit code e sinal;
  • timeout;
  • bytes de stdout e stderr;
  • fila e concorrência;
  • reinicializações.

Não registre argumentos que contenham tokens ou dados pessoais.

Containers

O processo Node.js deve encaminhar sinais e colher filhos encerrados. Use um init leve quando necessário e teste shutdown no container. Limites de CPU e memória se aplicam à soma dos processos.

Worker Threads ou child_process

Use Worker Threads quando deseja paralelismo de CPU com menor custo e possível transferência de memória. Use processos quando precisa de isolamento forte, outro executável, ambiente separado ou proteção contra falhas nativas.

Erros comuns

  • concatenar entrada do usuário em exec;
  • usar exec para saída grande;
  • não tratar error e close;
  • ignorar exit code;
  • não definir timeout;
  • deixar processos órfãos;
  • passar todo process.env;
  • não limitar concorrência;
  • ignorar backpressure;
  • assumir comportamento idêntico entre sistemas.

Fluxo recomendado

Prefira spawn ou execFile, use arrays de argumentos, limite saída e tempo, trate sinais e valide o resultado. Para CPU em JavaScript, compare com Worker Threads. Para streams, aplique perf_hooks e métricas antes de otimizar.

Consulte a documentação oficial de child_process e a referência oficial de spawn.

10 melhores cursos de programação em 2026

Descubra os melhores cursos de programação. Aprenda a escolher o curso ideal para iniciar ou avançar na carreira de desenvolvedor

POSTS RELACIONADOS

Ver todos

Seta para a direita