AmplyInvoice
An invoice editor where your invoices never leave your device.
An invoice editor that runs in your browser. There is no account and nothing is uploaded: your invoices stay on your own device. When one is ready, you print it or save it as a PDF.
01
How it's built
- TypeScript
- Astro
- Svelte 5
- Tailwind 4
- DaisyUI
- IndexedDB
- Dexie
- PWA
- Cloudflare Workers
- Turnstile
- Maileroo
- Vitest
Astro on Cloudflare Workers, with the editor as Svelte 5 islands. The app installs like any other and works offline.
- Architecture: vertical slices, one folder per feature and operation: create, get, list and delete an invoice, and save and list contacts and bank details. Following CQRS, each operation is a class that is either a query, which reads, or a command, which writes, with the storage passed in through its constructor. The invoice model and the money arithmetic sit in a domain layer that depends on nothing, not even the browser.
- Storage: the browser's own database, IndexedDB, through Dexie, behind one repository interface. The concrete store is chosen in one place, at the app's composition root, and no component touches the database directly.
- The interface: Svelte 5 with Tailwind 4 and DaisyUI. The PDF comes from the browser's print dialog, through a print stylesheet made for paper.
- The server: only the contact form, protected by Turnstile and sent through Maileroo. Server code is marked as such, so the browser can never import it.
- Tests: Vitest, with an IndexedDB fake so the storage is tested without a browser.
02
What was hard
A rewrite with the old app as the specification. AmplyInvoice replaces an earlier invoice app I built in Blazor WebAssembly. I did not port it. I read it for what it did, and rebuilt that behaviour in Astro and Svelte.
Money that adds up. Every amount is held as whole cents, in one module, and nothing rounds anywhere else. The lines and the total cannot disagree by a cent.
Invoices the browser keeps. A browser clears stored data when it runs short of space. The app asks it to keep the invoices out of that clean-up.
03
The trade-off
Nothing uploaded means there is no copy anywhere else, and no sync between devices. The invoices live in the browser that made them. For an editor without an account, that is the price of keeping them private.
The storage sits behind a single interface, so the same editor can later keep its invoices on a server, for accounts that share them across devices, without being rewritten.