docs.sheetjs.com/docz/docs/03-demos/03-net/03-server/19-fastify.md
SheetJS 92e3c5aa72 mdx cleanup in preparation for v2
- use autolinks (e.g <https://sheetjs.com> -> https://sheetjs.com)
- move <summary> blocks to separate lines
2024-04-08 00:57:39 -04:00

6.1 KiB

title sidebar_label pagination_prev pagination_next
Sheets in FastifyJS FastifyJS demos/net/network/index demos/net/email/index

import current from '/version.js'; import CodeBlock from '@theme/CodeBlock';

FastifyJS is a NodeJS web framework.

SheetJS is a JavaScript library for reading and writing data from spreadsheets.

This demo uses FastifyJS and SheetJS to read and write data. We'll explore how to parse uploaded files in a POST request handler and respond to GET requests with downloadable spreadsheets.

The "Complete Example" section includes a complete server.

:::note Tested Deployments

This demo was verified on 2024 March 11 using fastify@4.26.2

:::

Integration Details

The SheetJS NodeJS module can be imported from scripts that use FastifyJS.

Exporting Data to Workbooks (GET)

The SheetJS write method1 with the option type: "buffer" generates NodeJS Buffer objects containing the raw file data.

FastifyJS can directly handle Buffer data in Response#end

The exported filename can be specified using the Content-Disposition header.

The following demo FastifyJS server will respond to GET requests to /download with a XLSX spreadsheet. In this example, the SheetJS aoa_to_sheet method2 generates a sheet object and the book_new and book_append_sheet helpers3 build the workbook object.

/* GET / returns a workbook */
fastify.get('/', (req, reply) => {
  /* make a workbook */
  var wb = XLSX.read("S,h,e,e,t,J,S\n5,4,3,3,7,9,5", {type: "binary"});

  /* write to Buffer */
  const buf = XLSX.write(wb, {type:"buffer", bookType: "xlsx"});

  /* set Content-Disposition header and send data */
  // highlight-next-line
  reply.header('Content-Disposition', 'attachment; filename="SheetJSFastify.xlsx"').send(buf);
});

Parsing Uploaded Files (POST)

@fastify/multipart, which uses busbuy under the hood, must be registered:

/* load SheetJS Library */
const XLSX = require("xlsx");
/* load fastify and enable body parsing */
const fastify = require('fastify')({logger: true});
// highlight-next-line
fastify.register(require('@fastify/multipart'), { attachFieldsToBody: true });

Once registered with the option attachFieldsToBody, route handlers can use req.body directly.

Each file object in the body has a toBuffer method that resolves to a Buffer object. The SheetJS read method4 can read the Buffer and generate a workbook object5.

The following demo FastifyJS server will respond to POST requests to /upload. Assuming the upload field of the form data is the file, the SheetJS read method will parse the file. CSV rows are generated from the first worksheet using the SheetJS sheet_to_csv method6.

/* POST / reads submitted file and exports to requested format */
fastify.post('/', async(req, reply) => {
  /* "file" is the name of the field in the HTML form*/
  const file = req.body.upload;
  /* toBuffer returns a promise that resolves to a Buffer */
  // highlight-next-line
  const buf = await file.toBuffer();
  /* `XLSX.read` can read the Buffer */
  const wb = XLSX.read(buf);
  /* reply with a CSV */
  reply.send(XLSX.utils.sheet_to_csv(wb.Sheets[wb.SheetNames[0]]));
});

:::caution pass

Out of the box, Fastify will return an error FST_ERR_CTP_BODY_TOO_LARGE when processing large spreadsheets (statusCode 413). This is a Fastify issue.

The default body size limit (including all uploaded files and fields) is 1 MB. It can be increased by setting the bodyLimit option during server creation:

/* increase request body size limit to 5MB = 5 * 1024 * 1024 bytes */
const fastify = require('fastify')({bodyLimit: 5 * 1024 * 1024});

:::

Complete Example

  1. Save the following snippet to SheetJSFastify.js:
/* load SheetJS Library */
const XLSX = require("xlsx");
/* load fastify and enable body parsing */
const fastify = require('fastify')({logger: true});
fastify.register(require('@fastify/multipart'), { attachFieldsToBody: true });

/* GET / returns a workbook */
fastify.get('/', (req, reply) => {
  /* make a workbook */
  var wb = XLSX.read("S,h,e,e,t,J,S\n5,4,3,3,7,9,5", {type: "binary"});

  /* write to Buffer */
  const buf = XLSX.write(wb, {type:"buffer", bookType: "xlsx"});

  /* set Content-Disposition header and send data */
  reply.header('Content-Disposition', 'attachment; filename="SheetJSFastify.xlsx"').send(buf);
});

/* POST / reads submitted file and exports to requested format */
fastify.post('/', async(req, reply) => {

  /* "file" is the name of the field in the HTML form*/
  const file = req.body.upload;

  /* toBuffer returns a promise that resolves to a Buffer */
  const wb = XLSX.read(await file.toBuffer());

  /* send back a CSV */
  reply.send(XLSX.utils.sheet_to_csv(wb.Sheets[wb.SheetNames[0]]));
});

/* start */
fastify.listen({port: process.env.PORT || 3000}, (err, addr) => { if(err) throw err; });
  1. Install dependencies:

{\ npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz fastify@4.26.2 @fastify/multipart@8.1.0}

  1. Start server
node SheetJSFastify.js
  1. Test POST requests using https://sheetjs.com/pres.numbers . The following commands should be run in a new terminal window:
curl -LO https://sheetjs.com/pres.numbers
curl -X POST -F upload=@pres.numbers http://localhost:3000/

The response should show the data in CSV rows.

  1. Test GET requests by opening http://localhost:3000/ in your browser.

It should prompt to download SheetJSFastify.xlsx