233 lines
7.3 KiB
Markdown
233 lines
7.3 KiB
Markdown
|
---
|
||
|
title: Sheets in NestJS
|
||
|
sidebar_label: NestJS
|
||
|
pagination_prev: demos/net/network
|
||
|
pagination_next: demos/net/email
|
||
|
---
|
||
|
|
||
|
import current from '/version.js';
|
||
|
import CodeBlock from '@theme/CodeBlock';
|
||
|
|
||
|
[NestJS](https://nestjs.com/) is a NodeJS framework for building server-side
|
||
|
applications.
|
||
|
|
||
|
[SheetJS](https://sheetjs.com) is a JavaScript library for reading and writing
|
||
|
data from spreadsheets.
|
||
|
|
||
|
This demo uses NestJS 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 tested on 2023 October 15 using NestJS `10.2.7`.
|
||
|
|
||
|
:::
|
||
|
|
||
|
## Integration Details
|
||
|
|
||
|
The [SheetJS NodeJS module](/docs/getting-started/installation/nodejs) can be
|
||
|
imported from NestJS controller scripts.
|
||
|
|
||
|
:::caution pass
|
||
|
|
||
|
NestJS does not follow the conventional NodeJS server code structure.
|
||
|
|
||
|
It is strongly recommended to review the official documentation. The official
|
||
|
documentation covers Controller structure and various JS Decorators.
|
||
|
|
||
|
:::
|
||
|
|
||
|
### Exporting Data to Workbooks (GET)
|
||
|
|
||
|
The SheetJS `write` method[^1] with the option `type: "buffer"` generates NodeJS
|
||
|
Buffer objects containing the raw file data.
|
||
|
|
||
|
NestJS strongly recommends wrapping the Buffer in a `StreamableFile` object[^2].
|
||
|
|
||
|
The exported filename can be specified in a `@Header` decorator[^3]
|
||
|
|
||
|
The following demo NestJS Controller will respond to GET requests to `/download`
|
||
|
with a XLSX spreadsheet. In this example, the SheetJS `aoa_to_sheet` method[^4]
|
||
|
generates a sheet object and the `book_new` and `book_append_sheet` helpers[^5]
|
||
|
build the workbook object.
|
||
|
|
||
|
```ts title="src/sheetjs/sheetjs.controller.js"
|
||
|
import { Controller, Get, Header, StreamableFile } from '@nestjs/common';
|
||
|
import { utils, write } from 'xlsx';
|
||
|
|
||
|
@Controller('sheetjs')
|
||
|
export class SheetjsController {
|
||
|
@Get('download')
|
||
|
@Header('Content-Disposition', 'attachment; filename="SheetJSNest.xlsx"')
|
||
|
async downloadXlsxFile(): Promise<StreamableFile> {
|
||
|
var ws = utils.aoa_to_sheet(["SheetJS".split(""), [5,4,3,3,7,9,5]]);
|
||
|
var wb = utils.book_new(); utils.book_append_sheet(wb, ws, "Data");
|
||
|
// highlight-start
|
||
|
/* generate buffer */
|
||
|
var buf = write(wb, {type: "buffer", bookType: "xlsx"});
|
||
|
/* Return a streamable file */
|
||
|
return new StreamableFile(buf);
|
||
|
// highlight-end
|
||
|
}
|
||
|
}
|
||
|
```
|
||
|
|
||
|
### Parsing Uploaded Files (POST)
|
||
|
|
||
|
:::note pass
|
||
|
|
||
|
[The NestJS documentation](https://docs.nestjs.com/techniques/file-upload) has
|
||
|
detailed instructions for file upload support.
|
||
|
|
||
|
:::
|
||
|
|
||
|
As explained in the NestJS documentation, in a POST request handler, the
|
||
|
`FileInterceptor` interceptor and `UploadedFile` decorator are used in tandem to
|
||
|
expose a file uploaded with a specified key.
|
||
|
|
||
|
The file object has a `buffer` property which represents the raw bytes. The
|
||
|
SheetJS `read` method[^6] can parse the Buffer into a workbook object[^7].
|
||
|
|
||
|
The following demo NestJS Controller 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[^8].
|
||
|
|
||
|
```ts title="src/sheetjs/sheetjs.controller.js"
|
||
|
import { Controller, Post, UploadedFile, UseInterceptors } from '@nestjs/common';
|
||
|
import { FileInterceptor } from '@nestjs/platform-express';
|
||
|
import { read, utils } from 'xlsx';
|
||
|
|
||
|
@Controller('sheetjs')
|
||
|
export class SheetjsController {
|
||
|
@Post('upload') // <input type="file" id="upload" name="upload">
|
||
|
@UseInterceptors(FileInterceptor('upload'))
|
||
|
async uploadXlsxFile(@UploadedFile() file: Express.Multer.File) {
|
||
|
/* file.buffer is a Buffer */
|
||
|
// highlight-next-line
|
||
|
const wb = read(file.buffer);
|
||
|
/* generate CSV of first worksheet */
|
||
|
return utils.sheet_to_csv(wb.Sheets[wb.SheetNames[0]]);
|
||
|
}
|
||
|
}
|
||
|
```
|
||
|
|
||
|
## Complete Example
|
||
|
|
||
|
1) Create a new NestJS project:
|
||
|
|
||
|
```bash
|
||
|
npx @nestjs/cli@latest new -p npm sheetjs-nest
|
||
|
cd sheetjs-nest
|
||
|
```
|
||
|
|
||
|
2) Install the `@types/multer` package as a development dependency:
|
||
|
|
||
|
```bash
|
||
|
npm i --save-dev @types/multer
|
||
|
```
|
||
|
|
||
|
3) Install the SheetJS library:
|
||
|
|
||
|
<CodeBlock language="bash">{`\
|
||
|
npm i --save https://cdn.sheetjs.com/xlsx-${current}/xlsx-${current}.tgz`}
|
||
|
</CodeBlock>
|
||
|
|
||
|
4) Make a folder for uploaded files:
|
||
|
|
||
|
```bash
|
||
|
mkdir -p upload
|
||
|
```
|
||
|
|
||
|
5) Create a new controller:
|
||
|
|
||
|
```bash
|
||
|
npx @nestjs/cli generate controller sheetjs
|
||
|
```
|
||
|
|
||
|
6) Replace `src/sheetjs/sheetjs.controller.ts` with the following code block:
|
||
|
|
||
|
```ts title="src/sheetjs/sheetjs.controller.js"
|
||
|
import { Controller, Get, Header, Post, StreamableFile, UploadedFile, UseInterceptors } from '@nestjs/common';
|
||
|
import { FileInterceptor } from '@nestjs/platform-express';
|
||
|
import { read, utils, write } from 'xlsx';
|
||
|
|
||
|
@Controller('sheetjs')
|
||
|
export class SheetjsController {
|
||
|
@Post('upload') // <input type="file" id="upload" name="upload">
|
||
|
@UseInterceptors(FileInterceptor('upload'))
|
||
|
async uploadXlsxFile(@UploadedFile() file: Express.Multer.File) {
|
||
|
/* file.path is a path to the workbook */
|
||
|
const wb = read(file.buffer);
|
||
|
/* generate CSV of first worksheet */
|
||
|
return utils.sheet_to_csv(wb.Sheets[wb.SheetNames[0]]);
|
||
|
}
|
||
|
|
||
|
@Get('download')
|
||
|
@Header('Content-Disposition', 'attachment; filename="SheetJSNest.xlsx"')
|
||
|
async downloadXlsxFile(): Promise<StreamableFile> {
|
||
|
var ws = utils.aoa_to_sheet(["SheetJS".split(""), [5,4,3,3,7,9,5]]);
|
||
|
var wb = utils.book_new(); utils.book_append_sheet(wb, ws, "Data");
|
||
|
/* generate buffer */
|
||
|
var buf = write(wb, {type: "buffer", bookType: "xlsx"});
|
||
|
/* Return a streamable file */
|
||
|
return new StreamableFile(buf);
|
||
|
}
|
||
|
}
|
||
|
```
|
||
|
|
||
|
7) Start the server with
|
||
|
|
||
|
```bash
|
||
|
npx @nestjs/cli start
|
||
|
```
|
||
|
|
||
|
:::note
|
||
|
|
||
|
In the most recent test, the process failed with a message referencing Multer:
|
||
|
|
||
|
```
|
||
|
src/sheetjs/sheetjs.controller.ts:9:54 - error TS2694: Namespace 'global.Express' has no exported member 'Multer'.
|
||
|
|
||
|
9 async uploadXlsxFile(@UploadedFile() file: Express.Multer.File) {
|
||
|
~~~~~~
|
||
|
```
|
||
|
|
||
|
This error indicates that `@types/multer` is not available.
|
||
|
|
||
|
**This is a bug in the `npm` client**
|
||
|
|
||
|
The recommended fix is to install `@types/multer` again:
|
||
|
|
||
|
```bash
|
||
|
npm i --save-dev @types/multer
|
||
|
```
|
||
|
|
||
|
:::
|
||
|
|
||
|
8) Test POST requests in the terminal with <https://sheetjs.com/pres.numbers>:
|
||
|
|
||
|
```bash
|
||
|
curl -LO https://sheetjs.com/pres.numbers
|
||
|
curl -X POST -F upload=@pres.numbers http://localhost:3000/sheetjs/upload
|
||
|
```
|
||
|
|
||
|
The response should show the data in CSV rows.
|
||
|
|
||
|
9) Test GET requests by opening `http://localhost:3000/sheetjs/download` in a
|
||
|
web browser.
|
||
|
|
||
|
The browser should attempt to download `SheetJSNest.xlsx`. Save the file and
|
||
|
open in a spreadsheet editor.
|
||
|
|
||
|
[^1]: See [`write` in "Writing Files"](/docs/api/write-options)
|
||
|
[^2]: See ["Streaming files"](https://docs.nestjs.com/techniques/streaming-files) in the NestJS documentation.
|
||
|
[^3]: See ["Headers"](https://docs.nestjs.com/controllers#headers) in the NestJS documentation.
|
||
|
[^4]: See [`aoa_to_sheet` in "Utilities"](/docs/api/utilities/array#array-of-arrays-input)
|
||
|
[^5]: See ["Workbook Helpers" in "Utilities"](/docs/api/utilities/wb) for details on `book_new` and `book_append_sheet`.
|
||
|
[^6]: See [`read` in "Reading Files"](/docs/api/parse-options)
|
||
|
[^7]: See ["Workbook Object"](/docs/csf/book)
|
||
|
[^8]: See [`sheet_to_csv` in "CSV and Text"](/docs/api/utilities/csv#delimiter-separated-output)
|