--- title: Sheets in FastifyJS sidebar_label: FastifyJS pagination_prev: demos/net/network/index pagination_next: demos/net/email/index --- import current from '/version.js'; import CodeBlock from '@theme/CodeBlock'; [FastifyJS](https://openjsf.org/projects/) is a NodeJS web framework. [SheetJS](https://sheetjs.com) 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"](#complete-example) section includes a complete server. :::note This demo was verified on 2023 October 16 using `fastify@4.24.2` ::: ## Integration Details The [SheetJS NodeJS module](/docs/getting-started/installation/nodejs) can be imported from scripts that use FastifyJS. ### Exporting Data to Workbooks (GET) The SheetJS `write` method[^1] 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` method[^2] generates a sheet object and the `book_new` and `book_append_sheet` helpers[^3] build the workbook object. ```js /* 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: ```js /* 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` method[^4] can read the Buffer and generate a workbook object[^5]. 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` method[^6]. ```js /* 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: ```js /* increase request body size limit to 5MB = 5 * 1024 * 1024 bytes */ const fastify = require('fastify')({bodyLimit: 5 * 1024 * 1024}); ``` ::: ## Complete Example 0) Save the following snippet to `SheetJSFastify.js`: ```js title="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.24.2 @fastify/multipart@8.0.0`} 2) Start server ```bash node SheetJSFastify.js ``` 3) Test POST requests using : ```bash 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. 4) Test GET requests by opening `http://localhost:3000/` in your browser. It should prompt to download `SheetJSFastify.xlsx` [^1]: See [`write` in "Writing Files"](/docs/api/write-options) [^2]: See [`aoa_to_sheet` in "Utilities"](/docs/api/utilities/array#array-of-arrays-input) [^3]: See ["Workbook Helpers" in "Utilities"](/docs/api/utilities/wb) for details on `book_new` and `book_append_sheet`. [^4]: See [`readFile` in "Reading Files"](/docs/api/parse-options) [^5]: See ["Workbook Object"](/docs/csf/book) [^6]: See [`sheet_to_csv` in "CSV and Text"](/docs/api/utilities/csv#delimiter-separated-output)