How to Create CSV in Node.js Without Saving Files Locally


I’ll now provide a sample code using Express, although in a real-world scenario, I implemented this with Nest.js and utilized ‘@nestjs/common’s StreamableFile.

In this example, I used csv-stringify/sync for converting data to csv format. Simply we can return converted data as result. The important thing here is set filename and content type in header.

const { stringify } = require("csv-stringify/sync");
const express = require("express");

const app = express();
const port = 3000;

app.get("/csv", (req, res) => {
  const data = [ // defined
    ["Name", "Age", "Country"],
    ["John Doe", 30, "USA"],
    ["Jane Doe", 25, "Canada"],
    ["Bob Smith", 40, "UK"],
  ];

  const csvData = stringify(data);

  res.setHeader("Content-Type", "text/csv");
  res.setHeader("Content-Disposition", "attachment; filename=data.csv");

  res.send(csvData);
});
copied!

For csv contains ShiftJIS

Just returning csv as the response is simple, however, in the case that the filename and data contain Shift-JIS(Shift Japanese Industrial Standards), it is necessary to specify UTF-8 with BOM (Byte Order Mark) with ufeff. Otherwise, this is not previewed correctly on Excel (It works fine without utf8 bom on Spreadsheet and Mac Preview)

app.get("/csv-sjis", (req, res) => {
  const data = [
    ["名前(name)", "歳(age)", "国(Country)"],
    ["三笠", 30, "日本"],
    ["のび太", 25, "アメリカ"],
    ["もぐろ", 40, "イギリス"],
  ];

  const csvData = stringify(data, {
    bom: true,
  });
  const csvDataWithBom = `\ufeff${csvData}`;

  const filename = "日本語ファイル名.csv";

  res.setHeader("Content-Type", "text/csv");
  res.setHeader(
    "Content-Disposition",
    `attachment; filename*=UTF-8''${encodeURIComponent(filename)}`
  );

  res.send(csvDataWithBom);
});
copied!

This part specifies the filename. The filename* parameter is used to provide a Unicode filename. The UTF-8’’ indicates that the filename is encoded using UTF-8. encodeURIComponent(filename) is used to ensure that special characters in the filename are properly encoded for a URL.

  res.setHeader(
    "Content-Disposition",
    `attachment; filename*=UTF-8''${encodeURIComponent(filename)}`
  );
copied!

When working with CSV files and dealing with character encodings, using UTF-8 ensures compatibility with a wide range of international characters. The BOM is a sequence of bytes at the beginning of a text file that helps identify the file’s encoding, and in the case of UTF-8, it aids in correctly interpreting the character order. Including the BOM is particularly beneficial when handling CSV files in environments that may not automatically detect UTF-8 encoding, ensuring seamless processing of data with diverse character sets.

Generating TSV

Additionally I had to generate TSV file which contains ShiftJIS as well. TSV stands for “Tab-Separated Values.” It is a plain text format for representing data where each row of the data is in a separate line, and the values within each row are separated by tabs. TSV is similar to CSV.

In order to handle with ShiftJIS, I had to use a library “iconv-lite” to set utf8 with BOM. Here is the sample code. What the code does is same as generating csv.

const iconv = require('iconv-lite');

app.get('/api/tsv', (req, res) => {
  const data = [
  ['名前(name)', '歳(age)', '国(Country)'],
  ['三笠', 30, '日本'],
  ['のび太', 25, 'アメリカ'],
  ['もぐろ', 40, 'イギリス'],
];

  const tsvData = data.map(row => row.join('\t')).join('\n');

  const bomUtf8Data = iconv.encode('\ufeff' + tsvData, 'utf-8');

  res.setHeader('Content-Type', 'text/tab-separated-values; charset=utf-8');
  res.setHeader('Content-Disposition', 'attachment; filename=data.tsv');

  res.send(bomUtf8Data);
});
copied!
buy me a coffee