# Node Express BullMQ Redis statement generator

A statement desk turns up to 200 customer balance rows into individual PDFs and one downloadable ZIP while showing live progress. Tokay runs the upload page and BullMQ worker separately over one managed Redis resource.

This is the pattern to use when a request should start substantial work without making the visitor wait on one web process. Redis carries the queue, progress, CSV, and temporary PDFs between both Services.

## Facts

- Runtime: Node.js
- Framework: Express and BullMQ
- Service types: Web Service, Background Worker
- Resources: Redis
- Required secrets: None

## What it does

The page offers a 40 row sample CSV and accepts your own file with customer, email, period, and balance columns.

Press **Generate statements** and the current batch counter advances as the Worker finishes each PDF. Completed names become individual download links, and **Download all PDFs** appears when the full batch is ready.

The desk keeps the newest eight completed batches. A ninth removes the oldest temporary source and output files from Redis.

## Deploy it

Bring this folder to Tokay any way you like. Paste or upload it in the dashboard, push it with git, or hand it to your AI agent along with [our agent instructions](https://app.tokay.io/llms.txt). Tokay figures out the rest.

Working in Claude, ChatGPT, VS Code, or another MCP client? Connect the [Tokay MCP server](https://tokay.io/docs/platform-mcp) and ask your agent to deploy this example. It signs in through your browser, so there is no token to paste.

New to Tokay? The [getting started guide](https://tokay.io/getting-started) walks you through your first deploy.

## Try it

1. Download the sample CSV, upload it, and watch the counter reach 40.
2. Open one customer statement and confirm that the balance appears in a valid PDF.
3. Download the ZIP and check that it contains all 40 named statement files.

## Watch recovery

Start a larger batch, then stop the Background Worker while it is moving. Tokay restarts the process, and BullMQ returns the interrupted job to the queue. Watch the page continue from where the batch left off.

This learning app keeps the newest eight batches. Creating a ninth removes the oldest CSV and PDFs from Redis, so do not use the example as a permanent statement archive.

## How it works

You keep the page responsive because the Web Service validates the CSV and adds one BullMQ job per customer instead of rendering PDFs itself. The Worker processes two at a time and saves each result to Redis with an idempotent completion marker.

The page polls batch metadata and serves individual files or a streamed ZIP. When statements become business records, move them to permanent object storage rather than stretching this intentionally short Redis history.

## Secrets

Neither Service needs a secret from you. Tokay supplies the same private `REDIS_URL` to both of them.

## Grow from here

Try the [WebSocket chat](/examples/node-websocket-chat) when a long running process should coordinate connected browsers instead of queued work.

## Source files

- [web/index.html](https://tokay.io/examples/src/node-express-bullmq-redis-statement-generator/web/index.html)
- [web/package.json](https://tokay.io/examples/src/node-express-bullmq-redis-statement-generator/web/package.json)
- [web/server.js](https://tokay.io/examples/src/node-express-bullmq-redis-statement-generator/web/server.js)
- [worker/package.json](https://tokay.io/examples/src/node-express-bullmq-redis-statement-generator/worker/package.json)
- [worker/worker.js](https://tokay.io/examples/src/node-express-bullmq-redis-statement-generator/worker/worker.js)

## Download and source

- [Download the complete project](https://tokay.io/examples/downloads/node-express-bullmq-redis-statement-generator.zip)
- Last updated: 2026-07-31
- Example revision: `52c7b7f`
- [View this revision on GitHub](https://github.com/tokayio/tokay-examples/tree/52c7b7ffd52e157375b08f1f11ff4fbe6c498642/node-express-bullmq-redis-statement-generator)
- [Deploy a queue worker](https://tokay.io/use-cases/deploy-queue-worker)
