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: