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

CSV Streaming no Node.js: processe arquivos grandes

Atualizado em: 25 de outubro de 2026

Rack de servidores processando fluxos de dados no Node.js

CSV Streaming no Node.js permite importar e exportar grandes planilhas sem carregar o arquivo inteiro na memória. O formato é simples para usuários, ERPs e ferramentas de análise, mas possui variações de delimitador, aspas, encoding, cabeçalhos e quebra de linha.

Uma implementação robusta usa parser especializado, backpressure, limites, validação por linha e lotes de banco. Dividir texto apenas por vírgulas falha quando um campo contém delimitador, aspas ou nova linha.

Exemplo de CSV

id,nome,email
1,Ana,ana@example.com
2,"Bruno, Silva",bruno@example.com
3,"Texto com
quebra de linha",carla@example.com

O terceiro registro ocupa mais de uma linha física. Por isso, readline sozinho não é um parser CSV correto.

Importação com parser

import { createReadStream } from 'node:fs';
import { parse } from 'csv-parse';
import { pipeline } from 'node:stream/promises';

const parser = parse({
  columns: true,
  bom: true,
  skip_empty_lines: true,
  trim: true,
  max_record_size: 256 * 1024
});

await pipeline(
  createReadStream('users.csv'),
  parser,
  async function* (records) {
    for await (const record of records) {
      yield validateUser(record);
    }
  }
);

Escolha uma biblioteca madura e fixe a versão. Opções e nomes variam entre pacotes.

Detectar delimitador?

Arquivos brasileiros frequentemente usam ponto e vírgula porque a vírgula aparece em números. Detecção automática pode errar. Prefira exigir configuração ou oferecer opções explícitas.

const allowedDelimiters = new Set([',', ';', '\t']);

if (!allowedDelimiters.has(delimiter)) {
  throw new Error('Delimitador não permitido');
}

Encoding

UTF-8 é a melhor convenção, mas sistemas legados podem exportar Windows-1252 ou ISO-8859-1. Detectar encoding automaticamente não é infalível. Permita uma lista pequena e converta em streaming.

Remova BOM quando necessário e registre o encoding escolhido.

Validação de cabeçalho

const expected = ['id', 'nome', 'email'];

function validateHeaders(headers) {
  const normalized = headers.map(h => h.trim().toLowerCase());

  for (const required of expected) {
    if (!normalized.includes(required)) {
      throw new Error(`Coluna obrigatória ausente: ${required}`);
    }
  }
}

Bloqueie cabeçalhos duplicados, pois um objeto JavaScript pode sobrescrever valores sem aviso.

Mapeamento de colunas

Não passe o registro diretamente ao banco. Converta cada campo.

function validateUser(row) {
  const id = String(row.id ?? '').trim();
  const name = String(row.nome ?? '').trim();
  const email = String(row.email ?? '').trim().toLowerCase();

  if (!id || id.length > 100) throw new Error('ID inválido');
  if (!name || name.length > 200) throw new Error('Nome inválido');
  if (!email.includes('@') || email.length > 320) {
    throw new Error('E-mail inválido');
  }

  return { id, name, email };
}

Números e datas

Não use Number(''), pois o resultado é zero. Diferencie vazio, ausente e valor inválido.

function parseInteger(value, field) {
  const text = String(value ?? '').trim();
  if (!/^-?\d+$/.test(text)) throw new Error(`${field} inválido`);

  const result = Number(text);
  if (!Number.isSafeInteger(result)) throw new Error(`${field} fora do limite`);
  return result;
}

Datas devem usar formato definido, como ISO 8601. Evite parsing dependente do locale do servidor.

Processamento em lotes

const batch = [];

for await (const row of parser) {
  batch.push(validateUser(row));

  if (batch.length === 500) {
    await repository.upsertBatch(batch);
    batch.length = 0;
  }
}

if (batch.length) await repository.upsertBatch(batch);

Escolha tamanho com benchmark. Lotes grandes aumentam memória e tempo de transação.

Staging table

Para importações críticas, carregue dados em tabela temporária ou de staging. Depois:

  1. valide constraints;
  2. gere relatório de erros;
  3. compare contagens;
  4. promova dados em transação controlada;
  5. arquive ou remova staging.

COPY

O comando PostgreSQL COPY é muito eficiente para grandes volumes. Ele exige cuidado com tipos, escaping e tratamento de erros. Uma estratégia comum é COPY para staging e SQL para validação e merge.

Política de erros

Defina antes do processamento:

  • falhar no primeiro erro;
  • aceitar linhas válidas e relatar inválidas;
  • limitar quantidade de erros;
  • processar tudo em staging e decidir depois.

Não deixe o comportamento implícito.

CSV Injection

Ao exportar para planilhas, células iniciadas com =, +, - ou @ podem ser interpretadas como fórmulas.

function safeSpreadsheetCell(value) {
  const text = String(value ?? '');
  return /^[=+\-@]/.test(text) ? `'${text}` : text;
}

A proteção depende do consumidor e pode alterar o conteúdo. Para dados críticos, documente a transformação e ofereça formatos alternativos.

Exportação em streaming

import { stringify } from 'csv-stringify';

const csv = stringify({
  header: true,
  columns: ['id', 'nome', 'email'],
  bom: true
});

res.setHeader('Content-Type', 'text/csv; charset=utf-8');
res.setHeader('Content-Disposition', 'attachment; filename="usuarios.csv"');

await pipeline(
  Readable.from(repository.streamUsers(tenantId)),
  async function* (users) {
    for await (const user of users) {
      yield {
        id: safeSpreadsheetCell(user.id),
        nome: safeSpreadsheetCell(user.name),
        email: safeSpreadsheetCell(user.email)
      };
    }
  },
  csv,
  res
);

Backpressure

Streams de banco e resposta precisam cooperar. Não busque milhões de linhas com rows = await query(). Use cursor ou paginação por chave.

Consulte Streams e Backpressure no Node.js.

Compressão

CSV costuma comprimir muito bem. Para exportações grandes, ofereça .csv.gz e preserve streaming.

Upload seguro

Valide tamanho antes e durante leitura. Não confie apenas na extensão ou Content-Type. Armazene uploads temporários fora da pasta pública e remova arquivos parciais.

Veja Upload Seguro de Arquivos no Node.js.

Cancelamento

Se o cliente fechar a conexão, interrompa cursor, parser e gravação. Em jobs assíncronos, use estado de cancelamento e checkpoints.

Idempotência

Imports podem ser repetidos. Use chave natural, ID externo ou identificador de arquivo para evitar duplicações.

CREATE UNIQUE INDEX users_external_id_unique
ON users (tenant_id, external_id);

Multi-tenancy

Associe o job ao tenant autenticado. Não aceite tenant ID dentro do CSV como autoridade. Todas as queries devem usar o contexto validado.

Observabilidade

Monitore bytes, linhas, válidas, inválidas, lotes, duração, memória, throughput e falhas. Registre número da linha e código do erro, sem expor dados pessoais.

Testes

  • vírgula em campo com aspas;
  • aspas escapadas;
  • quebra de linha dentro da célula;
  • CRLF e LF;
  • BOM;
  • ponto e vírgula;
  • encoding alternativo;
  • cabeçalho duplicado;
  • registro gigante;
  • fórmula maliciosa;
  • cancelamento;
  • reprocessamento.

Erros comuns

  • usar split(',');
  • carregar arquivo inteiro;
  • não limitar registro;
  • detectar delimiter sem confirmação;
  • não validar cabeçalho;
  • converter vazio em zero;
  • ignorar CSV injection;
  • uma transação gigante;
  • não remover temporários;
  • não definir política de erros.

Checklist

  • parser confiável;
  • delimitador explícito;
  • encoding definido;
  • limites de arquivo e registro;
  • cabeçalhos validados;
  • tipos convertidos;
  • lotes ou staging;
  • CSV injection tratada;
  • backpressure;
  • cancelamento e métricas.

Conclusão

CSV Streaming no Node.js permite integrar planilhas e sistemas legados sem esgotar memória. O segredo é tratar CSV como um formato complexo, não como texto separado por vírgulas.

Use parser especializado, limites, validação por linha e lotes. Proteja exportações contra fórmulas e defina claramente atomicidade e erros. Com streams e staging, arquivos grandes podem ser processados com previsibilidade.

Consulte o RFC 4180 sobre CSV e a documentação de Streams do Node.js.

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