# Generating a Word document using node-oracledb and ETCH

node-oracledb 26.0.0 came out on 8 October ([release notes](https://node-oracledb.readthedocs.io/en/latest/release_notes.html)). It's the first release with Oracle's new year-based numbering, and I wanted an excuse to try it. So I pointed it at an Oracle AI Database 26ai Free container and got it to generate a Word document with [ETCH](https://get-etch.app), the document engine I've been building at [ETCH Labs.](https://www.linkedin.com/company/etch-labs-limited)

The database has a `PRODUCTS` table: a name, a category and a price for each product, and a photo of it in a BLOB. What I wanted was a product sheet in Word with every product and its picture, laid out properly, without writing any layout code.

## The whole program

```javascript
import oracledb from 'oracledb';
import { ETCHClient, imageFromBytes } from '@get-etch/etch-client';

// Connect in Thin mode, no Oracle Client libraries needed
const connection = await oracledb.getConnection({
  user: process.env.ORACLE_USER,
  password: process.env.ORACLE_PASSWORD,
  connectString: process.env.ORACLE_CONNECT_STRING,
});

// Simple query. The quoted aliases match the template's tags
// fetchInfo brings each photo back from the BLOB as a Buffer
const result = await connection.execute(
  `select product_name as "name",
          category     as "description",
          price        as "price",
          image        as "photo"
     from products
    order by category, product_name`,
  [],
  { outFormat: oracledb.OUT_FORMAT_OBJECT, fetchInfo: { photo: { type: oracledb.BUFFER } } },
);
await connection.close();

// Turn each photo's bytes into an image the template can place
const products = result.rows.map((row) => ({ ...row, photo: imageFromBytes(row.photo) }));

// Send the template and the rows to ETCH, and save the finished document
const client = new ETCHClient(); // reads ETCH_API_KEY
const sheet = await client.process({
  template: 'product-sheet.docx',
  data: {
    seller: { name: 'Sample Studio Example Ltd', email: 'shop@sample-studio.example' },
    validUntil: '2026-12-31',
    products,
  },
});
await sheet.saveToFile('products.docx');
```

`node product-sheet.mjs`, and less than a second later there's a Word document with all twenty products in it, photos included.

![On the left, the template product-sheet.docx in Word: a header with {{seller.name}} and {{seller.email}}, "Prices include VAT and are valid until {{validUntil}}", and one table row holding {{#each products}}, {{#image photo width="72"}}, {{name}}, {{description}} and {{price | currency:"£"}}{{/each}}. On the right, the document that came back: Sample Studio Example Ltd, valid until 31 December 2026, and a row for each product with its photo, name, category and price, starting with Charging Cable at £15.00, Laptop Backpack at £79.00 and Power Bank at £45.00.](https://cdn.hashnode.com/uploads/covers/6ab92eb45048a85288a71c45/140b5d97-e36f-44f4-96e3-6caa6de85d73.png align="center")

## Why it's so easy

node-oracledb runs in Thin mode by default, so there were no Oracle Client libraries to install: `npm install oracledb` and that was it.

The template's tags are lower case (`{{name}}`, `{{price}}`, `{{#image photo}}`), and Oracle's column names come back in upper case. Quoting the aliases in the query keeps their case, and with `OUT_FORMAT_OBJECT` each row arrives as an object whose keys are those aliases. So the rows from the query are already the data the template wants, and there's no mapping code in between. If your own table has different column names, the aliases are the only thing to change.

The photos needed one more line. `fetchInfo` with `oracledb.BUFFER` brings the BLOB back as a Buffer rather than a LOB to stream, and `imageFromBytes` from the ETCH client turns those bytes into an image the template can place. The `{{#image photo width="72"}}` tag in the template decides where it goes and how big it is.

Everything else (the fonts, the table, the logo, the line under the header) is in the Word file. If someone wants the sheet to look different, they change the template, and the code stays as it is.

## Trying it with your own data

The template is [product-sheet.docx in the ETCH examples on GitHub](https://github.com/etch-labs/etch-client-examples/blob/main/templates/product-sheet.docx), if you want to open it and see the tags. Point the query at any table with something to call a name, a description, a price and a picture, and alias the columns to match.

Installing the ETCH client takes a package token and two lines in an `.npmrc`. The ETCH blog's post on [generating Word, Excel and PowerPoint documents from Node.js](https://get-etch.hashnode.dev/generate-word-excel-and-powerpoint-documents-from-node-js-using-etch) goes through that, and a lot more of what templates can do. The free plan is enough to try all of this.

The product photos are all CC0 or public domain, found through [Openverse](https://openverse.org).
