From CSV to JSON: A Node.js Utility You Can Reuse Anywhere

From CSV to JSON: A Node.js Utility You Can Reuse Anywhere

From CSV to JSON: A Node.js Utility You Can Reuse Anywhere

 

CSV (Comma-Separated Values) is a widely used data format for tabular data, but for modern web applications, JSON (JavaScript Object Notation) is far more versatile. In this blog, we’ll walk through creating a Node.js command-line tool that converts CSV files to JSON while smartly handling edge cases like quoted fields and nested structures.

1. Setting Up the Project Environment

We’ll begin by creating a Node.js project and installing necessary dependencies. Start with the following commands:

mkdir csv-to-json-cli
cd csv-to-json-cli
npm init -y
npm install commander csv-parse fs-extra

Dependencies explained:

  • commander: For parsing CLI arguments.
  • csv-parse: A robust parser to handle complex CSV inputs.
  • fs-extra: File system utility with promise support.

2. Building the Command-Line Interface

First, let’s create an index.js file with CLI integration:

#!/usr/bin/env node

const { program } = require('commander');
const path = require('path');
const fs = require('fs-extra');
const { parseCsvToJson } = require('./lib/parser');

program
  .name('csv2json')
  .description('Convert CSV files to JSON')
  .version('1.0.0')
  .argument('', 'Path to the CSV file')
  .option('-o, --output ', 'Output file path for JSON')
  .action(async (csvPath, options) => {
    try {
      const data = await parseCsvToJson(csvPath);
      const outputPath = options.output || path.basename(csvPath, '.csv') + '.json';
      await fs.writeJson(outputPath, data, { spaces: 2 });
      console.log(`Conversion complete! JSON saved to ${outputPath}`);
    } catch (err) {
      console.error('Error:', err.message);
    }
  });

program.parse();

With this, you can run ./index.js data.csv and get a JSON file!

3. Parsing and Converting CSV Safely

Now, in lib/parser.js, implement the actual CSV parsing logic. We’ll use csv-parse to cover edge cases like quoted fields and embedded commas.

const fs = require('fs-extra');
const { parse } = require('csv-parse/sync');

function parseCsvToJson(filePath) {
  return new Promise(async (resolve, reject) => {
    try {
      const content = await fs.readFile(filePath, 'utf8');
      const records = parse(content, {
        columns: true,
        skip_empty_lines: true,
        trim: true,
        relax_quotes: true
      });
      resolve(records);
    } catch (error) {
      reject(error);
    }
  });
}

module.exports = { parseCsvToJson };

Explanation:

  • columns: true ensures that each row is converted into an object using the header row.
  • relax_quotes allows parsing of improperly quoted fields — important for real-world data input.

4. Handling Nested Structures

CSV lacks native support for hierarchies, but we can simulate this by interpreting dot notation in headers.

For example, this CSV header:

id,name,address.street,address.city

…should become:

{
  "id": "1",
  "name": "John",
  "address": {
    "street": "123 Maple St",
    "city": "Springfield"
  }
}

To achieve this, modify our parser to support nested keys:

function nestObject(flat) {
  const nested = {};
  for (const key in flat) {
    const keys = key.split('.');
    keys.reduce((acc, part, i) => {
      if (i === keys.length - 1) acc[part] = flat[key];
      else acc[part] = acc[part] || {};
      return acc[part];
    }, nested);
  }
  return nested;
}

function parseCsvToJson(filePath) {
  return new Promise(async (resolve, reject) => {
    try {
      const content = await fs.readFile(filePath, 'utf8');
      const records = parse(content, {
        columns: true,
        skip_empty_lines: true,
        trim: true,
        relax_quotes: true
      });
      const nestedRecords = records.map(nestObject);
      resolve(nestedRecords);
    } catch (error) {
      reject(error);
    }
  });
}

This promotes scalability for complex data imports.

5. Real-World Tips and Optimization

Tip 1: Use streaming for huge CSVs. The current implementation loads the whole CSV into memory. For very large files, consider csv-parse‘s streaming mode with fs.createReadStream.

Tip 2: Add a flag for pretty-printing or minifying JSON output. e.g., --pretty.

Tip 3: Make your tool globally installable by adding this line to package.json:

"bin": {
  "csv2json": "index.js"
}

Then run npm link to symlink it into your global path.

Wrapping Up

In just a few lines of code, you’ve built yourself a reusable and extensible CSV-to-JSON converter with Node.js. It supports deep structures, deals with tricky CSV quirks, and is ready for real-world automation tasks.

Next steps? Wrap it with a GUI using Electron, or turn it into an API service. Automation has never looked so JSON-friendly 🎉

 

Useful links: