docs.sheetjs.com/docz/docs/03-demos/03-net/02-server/19-fastify.md

187 lines
6.0 KiB
Markdown
Raw Normal View History

2023-10-16 09:12:56 +00:00
---
title: Sheets in FastifyJS
sidebar_label: FastifyJS
pagination_prev: demos/net/network
pagination_next: demos/net/email
---
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:
<CodeBlock language="bash">{`\
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz fastify@4.24.2 @fastify/multipart@8.0.0`}
</CodeBlock>
2) Start server
```bash
node SheetJSFastify.js
```
3) Test POST requests using <https://sheetjs.com/pres.numbers>:
```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)