HonoJS demo
This commit is contained in:
parent
3645643606
commit
0699eabbba
232
docz/docs/03-demos/03-net/03-server/07-hono.md
Normal file
232
docz/docs/03-demos/03-net/03-server/07-hono.md
Normal file
@ -0,0 +1,232 @@
|
||||
---
|
||||
title: Sheets on Fire with HonoJS
|
||||
sidebar_label: HonoJS
|
||||
pagination_prev: demos/net/network/index
|
||||
pagination_next: demos/net/email/index
|
||||
---
|
||||
|
||||
import current from '/version.js';
|
||||
import CodeBlock from '@theme/CodeBlock';
|
||||
|
||||
[HonoJS](https://hono.dev/) is a lightweight server-side framework.
|
||||
|
||||
[SheetJS](https://sheetjs.com) is a JavaScript library for reading and writing
|
||||
data from spreadsheets.
|
||||
|
||||
This demo uses HonoJS 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 Tested Deployments
|
||||
|
||||
This demo was last tested in the following deployments:
|
||||
|
||||
| Platform | HonoJS | Date |
|
||||
|:---------------|:--------|:-----------|
|
||||
| BunJS `1.1.21` | `4.5.1` | 2024-07-27 |
|
||||
|
||||
:::
|
||||
|
||||
## Integration Details
|
||||
|
||||
The [SheetJS BunJS module](/docs/getting-started/installation/bun) can be
|
||||
imported from HonoJS server scripts.
|
||||
|
||||
### Reading Data
|
||||
|
||||
The HonoJS body parser[^1] processes files in POST requests. The body parser
|
||||
returns an object that can be indexed by field name:
|
||||
|
||||
```js
|
||||
/* /import route */
|
||||
app.post('/import', async(c) => {
|
||||
/* parse body */
|
||||
const body = await c.req.parseBody();
|
||||
/* get a file uploaded in the `upload` field */
|
||||
// highlight-next-line
|
||||
const file = body["upload"];
|
||||
|
||||
/* `file` is a `File` objects */
|
||||
// ...
|
||||
});
|
||||
```
|
||||
|
||||
HonoJS exposes each file as a `Blob` object. The `Blob#arrayBuffer` method
|
||||
returns a Promise that resolves to an `ArrayBuffer`. That `ArrayBuffer` can be
|
||||
parsed with the SheetJS `read` method[^2].
|
||||
|
||||
This example server responds to POST requests. The server will look for a file
|
||||
in the request body under the `"upload"` key. If a file is present, the server
|
||||
will parse the file and, generate CSV rows using the `sheet_to_csv` method[^3],
|
||||
and respond with text:
|
||||
|
||||
```js
|
||||
import { Hono } from 'hono';
|
||||
import { read, utils } from 'xlsx';
|
||||
|
||||
const app = new Hono();
|
||||
app.post('/import', async(c) => {
|
||||
/* get file data */
|
||||
const body = await c.req.parseBody();
|
||||
const file = body["upload"];
|
||||
const ab = await file.arrayBuffer();
|
||||
/* parse */
|
||||
const wb = read(ab);
|
||||
/* generate CSV */
|
||||
const csv = utils.sheet_to_csv(wb.Sheets[wb.SheetNames[0]]);
|
||||
return c.text(csv);
|
||||
});
|
||||
export default app;
|
||||
```
|
||||
|
||||
### Writing Data
|
||||
|
||||
Given a SheetJS workbook object, the `write` method using `type: "buffer"`[^4]
|
||||
generates data objects which can be passed to the response `body` method.
|
||||
|
||||
This example server responds to GET requests. The server will generate a SheetJS
|
||||
worksheet object from an array of arrays[^5], build up a new workbook using the
|
||||
`book_new`[^6] utility method, generate a XLSX file using `write`, and send the
|
||||
file with appropriate headers to download `SheetJSHonoJS.xlsx`:
|
||||
|
||||
```js
|
||||
import { Hono } from 'hono';
|
||||
import { utils, write } from "xlsx";
|
||||
|
||||
const app = new Hono();
|
||||
app.get("/export", (c) => {
|
||||
/* generate SheetJS workbook object */
|
||||
var ws = utils.aoa_to_sheet(["SheetJS".split(""), [5,4,3,3,7,9,5]]);
|
||||
var wb = utils.book_new(ws, "Data");
|
||||
/* generate buffer */
|
||||
var buf = write(wb, {type: "buffer", bookType: "xlsx"});
|
||||
/* set headers */
|
||||
c.header('Content-Disposition', 'attachment; filename="SheetJSHonoJS.xlsx"');
|
||||
c.header('Content-Type', 'application/vnd.ms-excel');
|
||||
/* export buffer */
|
||||
return c.body(buf);
|
||||
});
|
||||
export default app;
|
||||
```
|
||||
|
||||
## Complete Example
|
||||
|
||||
This example creates a simple server that stores an array of arrays. There are
|
||||
three server endpoints:
|
||||
|
||||
- `/import` POST request expects a file in the `upload` field. It will parse the
|
||||
file, update the internal array of arrays, and responds with CSV data.
|
||||
|
||||
- `/export` GET request generates a workbook from the internal array of arrays.
|
||||
It will respond with XLSX data and initiate a download to `SheetJSHonoJS.xlsx` .
|
||||
|
||||
- `/json` GET request responds with the internal state.
|
||||
|
||||
1) Create a new BunJS + HonoJS project:
|
||||
|
||||
```bash
|
||||
bun create hono sheetjs-hono --template bun --install --pm bun
|
||||
cd sheetjs-hono
|
||||
```
|
||||
|
||||
2) Install the [SheetJS BunJS module](/docs/getting-started/installation/bun):
|
||||
|
||||
<CodeBlock language="bash">{`\
|
||||
bun i xlsx@https://sheet.lol/balls/xlsx-${current}.tgz`}
|
||||
</CodeBlock>
|
||||
|
||||
3) Save the following script to `src/index.ts`:
|
||||
|
||||
```ts title="src/index.ts"
|
||||
import { Hono } from 'hono';
|
||||
import { read, write, utils } from 'xlsx';
|
||||
|
||||
const app = new Hono();
|
||||
let data = ["SheetJS".split(""), [5,4,3,3,7,9,5]];
|
||||
|
||||
app.get('/export', (c) => {
|
||||
const ws = utils.aoa_to_sheet(data);
|
||||
const wb = utils.book_new(ws, "SheetJSHono");
|
||||
const buf = write(wb, { type: "buffer", bookType: "xlsx" });
|
||||
c.header('Content-Disposition', 'attachment; filename="SheetJSHonoJS.xlsx"');
|
||||
c.header('Content-Type', 'application/vnd.ms-excel');
|
||||
return c.body(buf);
|
||||
});
|
||||
|
||||
app.post('/import', async(c) => {
|
||||
const body = await c.req.parseBody();
|
||||
const file = body["upload"];
|
||||
const ab = await file.arrayBuffer();
|
||||
const wb = read(ab);
|
||||
data = utils.sheet_to_json(wb.Sheets[wb.SheetNames[0]], { header:1 });
|
||||
return c.text(utils.sheet_to_csv(wb.Sheets[wb.SheetNames[0]]));
|
||||
});
|
||||
|
||||
app.get('/json', (c) => c.json(data));
|
||||
export default app;
|
||||
```
|
||||
|
||||
4) Run the server:
|
||||
|
||||
```bash
|
||||
bun run dev
|
||||
```
|
||||
|
||||
The process will display a URL (typically `http://localhost:3000`):
|
||||
|
||||
```text
|
||||
% bun run dev
|
||||
$ bun run --hot src/index.ts
|
||||
// highlight-next-line
|
||||
Started server http://localhost:3000
|
||||
```
|
||||
|
||||
5) Test exports by opening `http://localhost:3000/export` in your browser.
|
||||
|
||||
The page should attempt to download `SheetJSHonoJS.xlsx` . Save the download and
|
||||
open the new file. The contents should match the original data:
|
||||
|
||||
<table>
|
||||
<tr><td>S</td><td>h</td><td>e</td><td>e</td><td>t</td><td>J</td><td>S</td></tr>
|
||||
<tr><td>5</td><td>4</td><td>3</td><td>3</td><td>7</td><td>9</td><td>5</td></tr>
|
||||
</table>
|
||||
|
||||
6) Test imports using https://docs.sheetjs.com/pres.numbers . The commands
|
||||
should be run in a new terminal window:
|
||||
|
||||
```bash
|
||||
curl -LO https://docs.sheetjs.com/pres.numbers
|
||||
curl -X POST -F upload=@pres.numbers http://localhost:3000/import
|
||||
```
|
||||
|
||||
The terminal will display CSV rows generated from the first worksheet:
|
||||
|
||||
```text title="Expected output"
|
||||
Name,Index
|
||||
Bill Clinton,42
|
||||
GeorgeW Bush,43
|
||||
Barack Obama,44
|
||||
Donald Trump,45
|
||||
Joseph Biden,46
|
||||
```
|
||||
|
||||
7) Confirm the state was updated by loading `http://localhost:3000/json` :
|
||||
|
||||
```bash
|
||||
curl -LO http://localhost:3000/json
|
||||
```
|
||||
|
||||
The terminal will display the worksheet data in an array of arrays:
|
||||
|
||||
```json title="Expected output"
|
||||
[["Name","Index"],["Bill Clinton",42],["GeorgeW Bush",43],["Barack Obama",44],["Donald Trump",45],["Joseph Biden",46]]
|
||||
```
|
||||
|
||||
[^1]: See ["parseBody()"](https://hono.dev/docs/api/request#parsebody) in the HonoJS documentation.
|
||||
[^2]: See [`read` in "Reading Files"](/docs/api/parse-options)
|
||||
[^3]: See [`sheet_to_csv` in "Utilities"](/docs/api/utilities/csv#delimiter-separated-output)
|
||||
[^4]: See [`write` in "Writing Files"](/docs/api/write-options)
|
||||
[^5]: See [`aoa_to_sheet` in "Utilities"](/docs/api/utilities/array#array-of-arrays-input)
|
||||
[^6]: See [`book_new` in "Utilities"](/docs/api/utilities/wb)
|
@ -407,7 +407,7 @@ Function arguments are separated with commas. For example, the Spanish Excel
|
||||
formula `=CONTAR(A1:C3;B4:D6)` is equivalent to the SheetJS formula string
|
||||
`COUNT(A1:A3,B4:D6)`
|
||||
|
||||
[JSON Translation table](https://oss.sheetjs.com/notes/fmla/table.json).
|
||||
[JSON Translation table](https://docs.sheetjs.com/fmla/table.json).
|
||||
|
||||
<details open>
|
||||
<summary><b>Function Name Translator</b> (click to hide)</summary>
|
||||
@ -422,7 +422,7 @@ function Translator(props) {
|
||||
/* Fetch and display formula */
|
||||
React.useEffect(() => { (async() => {
|
||||
/* Fetch data */
|
||||
const json = await (await fetch("https://oss.sheetjs.com/notes/fmla/table.json")).json();
|
||||
const json = await (await fetch("/fmla/table.json")).json();
|
||||
setLocales(Object.keys(json));
|
||||
setData(json);
|
||||
setNames(json.en);
|
||||
|
@ -46,7 +46,7 @@ These instructions were tested on the following platforms:
|
||||
| MacOS 14.4 (x64) | `darwin-x64` | 2024-07-12 |
|
||||
| MacOS 14.5 (ARM64) | `darwin-arm` | 2024-05-23 |
|
||||
| Windows 10 (x64) + WSL Ubuntu | `win10-x64` | 2024-07-12 |
|
||||
| Windows 11 (x64) + WSL Ubuntu | `win11-x64` | 2024-05-10 |
|
||||
| Windows 11 (x64) + WSL Ubuntu | `win11-x64` | 2024-07-26 |
|
||||
| Windows 11 (ARM) + WSL Ubuntu | `win11-arm` | 2024-05-23 |
|
||||
|
||||
With some additional dependencies, the unminified scripts are reproducible and
|
||||
|
1
docz/static/fmla/table.json
Normal file
1
docz/static/fmla/table.json
Normal file
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue
Block a user