docs.sheetjs.com/docz/docs/03-demos/03-net/03-server/07-hono.md
2024-07-27 21:18:37 -04:00

6.9 KiB

title sidebar_label pagination_prev pagination_next
Sheets on Fire with HonoJS HonoJS demos/net/network/index demos/net/email/index

import current from '/version.js'; import CodeBlock from '@theme/CodeBlock';

HonoJS is a lightweight server-side framework.

SheetJS 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" 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 can be imported from HonoJS server scripts.

Reading Data

The HonoJS body parser1 processes files in POST requests. The body parser returns an object that can be indexed by field name:

/* /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 method2.

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 method3, and respond with text:

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 arrays5, build up a new workbook using the book_new6 utility method, generate a XLSX file using write, and send the file with appropriate headers to download SheetJSHonoJS.xlsx:

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:
bun create hono sheetjs-hono --template bun --install --pm bun
cd sheetjs-hono
  1. Install the SheetJS BunJS module:

{\ bun i xlsx@https://sheet.lol/balls/xlsx-${current}.tgz}

  1. Save the following script to 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;
  1. Run the server:
bun run dev

The process will display a URL (typically http://localhost:3000):

% bun run dev
$ bun run --hot src/index.ts
// highlight-next-line
Started server http://localhost:3000
  1. 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:

SheetJS
5433795
  1. Test imports using https://docs.sheetjs.com/pres.numbers . The commands should be run in a new terminal window:
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:

Name,Index
Bill Clinton,42
GeorgeW Bush,43
Barack Obama,44
Donald Trump,45
Joseph Biden,46
  1. Confirm the state was updated by loading http://localhost:3000/json :
curl -LO http://localhost:3000/json

The terminal will display the worksheet data in an array of arrays:

[["Name","Index"],["Bill Clinton",42],["GeorgeW Bush",43],["Barack Obama",44],["Donald Trump",45],["Joseph Biden",46]]