NodeJS
CSV
Threads
Workers
File Handling

NodeJS read write CSVs in threads/workers

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

Introduction

Large CSV processing can slow a Node.js service if parsing and transformation run on the main thread. Worker threads let you offload CPU-heavy steps while the event loop remains responsive for API traffic. This guide shows a practical pattern for reading, processing, and writing CSV data with workers.

Core Topic Sections

When workers help for CSV workloads

Worker threads are useful when CSV tasks include expensive parsing, type conversion, validation, or aggregation. For small files, plain stream pipelines are often enough. For large files or multi-tenant services, moving heavy work to a worker protects latency of the main process.

Decision rule:

  1. Mostly I/O with tiny transforms, use streams on main thread.
  2. Heavy row-level computation, use worker threads.

Install lightweight CSV tooling

bash
npm install csv-parse csv-stringify

Use stream-based libraries to avoid loading whole files into memory.

Main thread orchestration

main.js starts a worker, passes input and output paths, and listens for progress and completion messages.

javascript
1const path = require('path');
2const { Worker } = require('worker_threads');
3
4function runCsvJob(inputPath, outputPath) {
5  return new Promise((resolve, reject) => {
6    const worker = new Worker(path.resolve(__dirname, 'csv-worker.js'), {
7      workerData: { inputPath, outputPath }
8    });
9
10    worker.on('message', (msg) => {
11      if (msg.type === 'progress') {
12        console.log(`processed rows: ${msg.rows}`);
13      }
14      if (msg.type === 'done') {
15        resolve(msg.rows);
16      }
17    });
18
19    worker.on('error', reject);
20    worker.on('exit', (code) => {
21      if (code !== 0) {
22        reject(new Error(`worker stopped with code ${code}`));
23      }
24    });
25  });
26}
27
28runCsvJob('./input.csv', './output.csv')
29  .then((rows) => console.log(`completed, rows: ${rows}`))
30  .catch((err) => console.error(err));

This keeps CPU-heavy parsing and transformation isolated.

Worker implementation with streams

csv-worker.js handles parse, transform, and write in a memory-safe way.

javascript
1const fs = require('fs');
2const { workerData, parentPort } = require('worker_threads');
3const { parse } = require('csv-parse');
4const { stringify } = require('csv-stringify');
5
6const input = fs.createReadStream(workerData.inputPath);
7const output = fs.createWriteStream(workerData.outputPath);
8
9const parser = parse({ columns: true, skip_empty_lines: true });
10const stringifier = stringify({ header: true });
11
12let count = 0;
13
14parser.on('readable', () => {
15  let row;
16  while ((row = parser.read()) !== null) {
17    const amount = Number(row.amount || 0);
18    row.amount_with_tax = (amount * 1.13).toFixed(2);
19
20    stringifier.write(row);
21    count += 1;
22
23    if (count % 10000 === 0) {
24      parentPort.postMessage({ type: 'progress', rows: count });
25    }
26  }
27});
28
29parser.on('error', (err) => {
30  throw err;
31});
32
33stringifier.on('error', (err) => {
34  throw err;
35});
36
37output.on('finish', () => {
38  parentPort.postMessage({ type: 'done', rows: count });
39});
40
41input.pipe(parser);
42stringifier.pipe(output);

This pattern scales better than reading entire CSV files into arrays.

Data transfer strategy between threads

Avoid sending full CSV content through postMessage because serialization cost can become a bottleneck. Send only control messages, progress counters, and summary results. Let worker thread read and write files directly.

For multiple jobs, use a small worker pool and queue tasks rather than spawning unbounded workers. This prevents memory spikes and CPU contention.

Reliability and observability

Production workflows should include:

  1. Input schema validation.
  2. Invalid-row counters and output reports.
  3. Job timeouts and cancellation path.
  4. Structured logging with job identifier.

These features make worker-based pipelines operable under real load.

Testing approach

Test worker code separately from orchestration logic:

  1. Unit test row transformation with deterministic fixtures.
  2. Integration test full file path from input CSV to output CSV.
  3. Performance test with realistic file sizes.

A dedicated benchmark catches regressions before deployment.

Common Pitfalls

  • Moving tiny CSV jobs to workers and adding complexity without measurable benefit.
  • Passing huge in-memory row arrays through postMessage and creating serialization overhead.
  • Spawning too many workers and saturating CPU and memory.
  • Ignoring backpressure when connecting parser and writer streams.
  • Missing error and exit handling in the main thread coordinator.

Summary

  • Worker threads protect Node.js responsiveness during heavy CSV transformations.
  • Keep parsing and writing stream-based for memory efficiency.
  • Use workers for computation-heavy processing, not every CSV task.
  • Exchange small control messages, not full datasets, across thread boundaries.
  • Add validation, logging, and limits for reliable production operation.

Related reading
Free course
Beginner
7 lessons
2 hours
Tackling System Design Interview Problems

A short course that equips you with the skills to approach system design interviews methodically.

Start the free course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

Interview Questions practice on Codemia

Over 8,000 real interview questions from top companies, searchable by company and role.

Browse interview questions

All Rights Reserved.