- Schema: account_id FK on checks and layout_fields; UNIQUE per-account on check_no and field_name - DB: runtime migration recreates both tables to add account_id (assigns existing rows to account 1) - Routes: GET /api/accounts lists all; GET /api/account/:id replaces hardcoded id=1; POST /api/account/setup always creates a new account and returns accountId - checks.js: all queries scoped by account_id; POST requires account_id in body - pdf.js: resolves account from check's account_id instead of id=1; layout fields fetched per-account - import-mdb.js: always INSERTs a new account (never deletes existing); all records tagged with new accountId - Frontend: account switcher in header; activeAccountId persisted to localStorage; all API calls pass account_id; switching accounts reloads checks; wizard and import auto-switch to newly created account
check-printing
Self-hosted web app for printing checks on blank check stock. A Dockerized Node.js web app accessible on the local network.
Stack
- Runtime: Node.js 20
- Framework: Express 4
- Database: SQLite via
better-sqlite3 - PDF generation: PDFKit with embedded GnuMICR E-13B font (GPL-2.0)
- Frontend: Vanilla JS, no framework
- Container: Docker Compose pulling from Docker Hub
Getting started
Production (Docker)
docker compose pull
docker compose up -d
On first launch, the app detects no account is configured and opens a setup wizard in the browser. Fill in three steps — checkwriter info, bank info, and account/routing numbers — then start entering checks.
If you have an existing ezCheckPrinting .mdb file, click Import .mdb instead.
Development (local)
npm install
npm run dev # nodemon src/app.js
Importing from ezCheckPrinting (.mdb)
Two ways to import:
Via the UI (recommended): Click Import .mdb in the toolbar, select the file, and click Import. The server runs the migration and shows the log output.
Via CLI (inside the container or locally with mdbtools installed):
docker exec -it check-printing node migrations/import-mdb.js \
--file "/app/data/YourAccount.mdb"
# Preview without writing:
node migrations/import-mdb.js --file YourAccount.mdb --dry-run
The script imports account config (T100), logo (Settings), check layout (T200), and check history (T104).
Printing
- Select 1–3 checks from the ledger (checkbox column)
- Click Generate PDF
- A 3-up 8.5"×11" PDF opens in a new tab — three 3.5" check slots per page
- Print from the browser; checks are marked as printed in the ledger
Use the Reprint button on printed checks to regenerate without re-marking them.
Check layout
- Page: 8.5" × 11", zero margins
- Three check slots of 3.5" each; remaining ~0.5" is tear-off strip
- MICR line at 0.267" from bottom of each slot
- MICR format:
A{routing}A {account}C {checkNo}A(GnuMICR E-13B encoding)
Environment variables
The app has no required configuration. These are set in docker-compose.yml:
| Variable | Default | Description |
|---|---|---|
PORT |
3000 |
HTTP port |
DB_PATH |
/app/data/check-printing.db |
SQLite database path |