| 
									
										
										
										
											2023-10-16 09:12:56 +00:00
										 |  |  | --- | 
					
						
							|  |  |  | title: Sheets in NestJS | 
					
						
							|  |  |  | sidebar_label: NestJS | 
					
						
							| 
									
										
										
										
											2023-11-20 02:51:39 +00:00
										 |  |  | pagination_prev: demos/net/network/index | 
					
						
							| 
									
										
										
										
											2023-10-23 01:20:18 +00:00
										 |  |  | pagination_next: demos/net/email/index | 
					
						
							| 
									
										
										
										
											2023-10-16 09:12:56 +00:00
										 |  |  | --- | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 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 | 
					
						
							|  |  |  | ``` | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-04 05:05:26 +00:00
										 |  |  | :::note pass | 
					
						
							| 
									
										
										
										
											2023-10-16 09:12:56 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 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) |