Node.js can be used to build a variety of apps and websites. It's most popularly used with Express to create a server for your websites and apps.
In this tutorial, you'll learn how you can read and write CSV files using Node.js and Express. You can find the full code for this tutorial in this GitHub Repository.
Project Setup
You'll start by setting up the server with NPM.
Run the following command to create a new directory and initialize the project with NPM:
mkdir node-csv
npm init -y
Then, you need to install the dependencies needed for the project:
npm i express body-parser nodemon
This will install express
to create a server, body-parser
to parse the body of the requests, and nodemon
to make sure that the server restarts whenever there are new changes in the files.
After that, create index.js
with the following content:
const express = require('express')
const bodyParser = require('body-parser')
const app = express()
const port = 3000
app.use(bodyParser.json())
app.use(express.static('public'))
app.listen(port, () => {
console.log(`App listening on port ${port}`)
})
This will initialize your server.
Finally, add the start
script command in package.json
:
"scripts": {
"start": "nodemon index.js"
},
Now, you can start the server by running the following command:
npm start
This will start the server on localhost:3000
.
Write CSV Files
The first part of this tutorial will go over how you can write CSV files.
For this, you'll use the CSV Stringify library that takes a set of data and turns it into a string in the CSV format. You can then use the string to write a CSV file.
In this tutorial, you'll create a page where the user can dynamically create a table to be transformed into a CSV file that they can download.
Start by installing the necessary dependency for this functionality:
npm i csv-stringify
Create the file public/create.html
with the following content:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet"
integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
<link href="https://unpkg.com/tabulator-tables/dist/css/tabulator.min.css" rel="stylesheet">
<title>Create CSV</title>
</head>
<body>
<div class="container py-4">
<h1>Create CSV</h1>
<h2>Add Columns</h2>
<input type="text" name="column" id="columnName" class="form-control" placeholder="Column Name" />
<button class="btn btn-primary mt-1" id="addColumn">Add</button>
<h2 class="mt-3">Column Data</h2>
<button class="btn btn-primary mb-3" id="addRow">Add Row</button>
<div id="csvTable"></div>
<button class="btn btn-primary mt-3" id="submitForm">Create CSV</button>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"
integrity="sha384-ka7Sk0Gln4gmtz2MlQnikT1wXgYsOg+OMhuP+IlRH9sENBO0LRn5q+8nbTov4+1p" crossorigin="anonymous"></script>
<script type="text/javascript" src="https://unpkg.com/tabulator-tables/dist/js/tabulator.min.js"></script>
<script>
const columnNameInput = document.getElementById('columnName');
const addColumnButton = document.getElementById('addColumn');
const addRowButton = document.getElementById('addRow');
const submitFormButton = document.getElementById('submitForm');
const table = new Tabulator("#csvTable", {
height:"300px",
data: [], //assign data to table
movableColumns: true,
addRowPos: "bottom",
});
addColumnButton.addEventListener('click', () => {
const name = columnNameInput.value ? columnNameInput.value.trim() : '';
if (!name) {
alert("Please add a name");
return;
}
table.addColumn({title: name, field: name.toLowerCase(), editableTitle: true, editor: true});
columnNameInput.value = '';
});
addRowButton.addEventListener('click', () => {
table.addRow({});
});
submitFormButton.addEventListener('click', () => {
const data = table.getData();
fetch('/create', {
method: 'POST',
body: JSON.stringify({
data
}),
headers: {
'Content-Type': 'application/json'
}
})
.then((response) => response.blob())
.then((blob) => {
const fileURL = URL.createObjectURL(blob)
const a = document.createElement('a')
a.href = fileURL
a.download = "file.csv"
a.click()
})
.catch((e) => {
console.error(e)
alert(e.message)
})
})
</script>
</body>
</html>
This page will allow the user to create a CSV file. For simplicity, you're using Bootstrap for easy styling and Tabulator to easily create a table with modifiable columns and rows.
You show the user an input to add columns with a name, and a button to add rows. After the user creates the CSV file using the table. They can click on the "Create CSV" button. This will take the data from the table and send a POST
request to the create
endpoint (which you'll create next) with the data. Then, the received file will be downloaded.
Next, you'll create the create
endpoint. Open index.js
and add the following require
statement at the beginning of the file:
const fs = require('fs')
const stringify = require('csv-stringify').stringify
You'll use fs
to create the CSV file and stringify
from the csv-stringify
library.
Next, add the following new endpoint to your server:
app.post('/create', (req, res) => {
const data = req.body.data
if (!data || !data.length) {
return res.status(400).json({success: false, message: 'Please enter at least 1 row'})
}
stringify(data, {
header: true
}, function (err, str) {
const path = './files/' + Date.now() + '.csv'
//create the files directory if it doesn't exist
if (!fs.existsSync('./files')) {
fs.mkdirSync('./files')
}
fs.writeFile(path, str, function (err) {
if (err) {
console.error(err)
return res.status(400).json({success: false, message: 'An error occurred'})
}
res.download(path, 'file.csv')
})
})
})
This will first validate the data sent. Then, you'll use the stringify
function to create the CSV string. This function takes the data to be stringified as the first parameter, an object of options as the second parameter, and a callback function as the third.
The header
option makes sure to include the column names as the header of the CSV file.
In the callback function, you create a file using fs
in the directory files
using writeFile
. The file will contain the CSV string created by stringify
. In the callback function of writeFile
you return the CSV file for download.
Now, if you run the server (if it's not already running) and go to localhost:3000/create.html
you'll see the page you created earlier in public/create.html
. Try adding a few columns and rows into the table.
Once you're done, click the "Create CSV" button. This will send the data to the server at the create
endpoint you created. Then, the endpoint will return a file for download which will then initiate a download in the user's browser.
Read a CSV File
In this section, you'll learn how to read a CSV file in Node.js and Express. The user will upload a CSV file.
Then, you'll pass the file along to the server that will parse it and return the data in JSON format. You'll then use Tabulator to show the CSV file's data.
To parse a CSV file, you'll use CSV Parse. You'll also use Express Multer Middleware to handle file upload.
Start by downloading the necessary dependencies:
npm i multer csv-parse
N0w, create the file public/read.html
with the following content:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Read CSV</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet"
integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
<link href="https://unpkg.com/tabulator-tables/dist/css/tabulator.min.css" rel="stylesheet">
</head>
<body>
<div class="container py-4">
<h1>Read CSV</h1>
<label for="file">Choose file to read</label>
<input type="file" class="form-control" name="file" id="file" />
<button class="btn btn-primary mt-2" id="submitFile">Read</button>
<div class="mt-2" id="csvTable"></div>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"
integrity="sha384-ka7Sk0Gln4gmtz2MlQnikT1wXgYsOg+OMhuP+IlRH9sENBO0LRn5q+8nbTov4+1p" crossorigin="anonymous"></script>
<script type="text/javascript" src="https://unpkg.com/tabulator-tables/dist/js/tabulator.min.js"></script>
<script>
const fileInput = document.getElementById('file')
const submitFile = document.getElementById('submitFile')
let file = null
fileInput.addEventListener('change', function () {
file = this.files[0]
})
submitFile.addEventListener('click', function () {
if (!file || file.type !== 'text/csv') {
alert('Please choose a CSV file')
return
}
const formData = new FormData()
formData.append('file', file)
fetch('/read', {
method: 'POST',
body: formData
})
.then((response) => response.json())
.then(({ data }) => {
if (data.length) {
const columns = data[0]
const rows = data.splice(1).map((arr) => {
const obj = {}
columns.forEach((column, index) => {
obj[column] = arr[index]
})
return obj
})
console.log(rows, columns)
const table = new Tabulator("#csvTable", {
height:"300px",
data: rows,
autoColumns: true
});
} else {
alert('The CSV is empty')
}
})
.catch((e) => alert(e.message))
})
</script>
</body>
</html>
Just like create.html
this file uses Bootstrap for easy styling and Tabulator to easily show the CSV file in a table.
The page shows a file input for the user with a button to upload the CSV file. When the user chooses a file and clicks the button, the file is uploaded to the server at the endpoint read
(which you'll create next). Then, using the data the server creates you'll show the data in a Tabulator table.
Now, you need to add the read
endpoint.
In index.js
, add the following require statements at the beginning of the file:
const parse = require('csv-parse').parse
const os = require('os')
const multer = require('multer')
const upload = multer({ dest: os.tmpdir() })
Notice that you also initialize multer and specify the destination as the tmp
directory of the operating system. This is because you don't need to actually store the file anywhere for this tutorial.
Also, note that you'll need to use fs
as well. So, if you didn't follow along with the previous section make sure to require it here as well.
Then, add the following new endpoint:
app.post('/read', upload.single('file'), (req, res) => {
const file = req.file
const data = fs.readFileSync(file.path)
parse(data, (err, records) => {
if (err) {
console.error(err)
return res.status(400).json({success: false, message: 'An error occurred'})
}
return res.json({data: records})
})
})
You first read the file using fs.readFileSync
. Then, you parse the file data using parse
from csv-parse
.
If an error occurs, you return an error message to the user. Otherwise, you return the data.
Run the server if it's not running already then go to localhost:3000/read.html
. You'll see a file input with a button.
Choose a CSV file then click read. The file data will be displayed in a table using Tabulator.
Conclusion
In this tutorial, you learned how you can read and write CSV files in Node.js and Express. You used the libraries CSV Parse and CSV Stringify to do that. Please check out each of their documentation for better understanding of how it works and what more you can do with them.