The Problem

Freelancers often juggle client details, quote creation, invoice generation, payment tracking, and contract signatures using separate tools. Switching between spreadsheets, email, and ad‑hoc PDF generators creates manual errors and delays payment collection.

What This Does

invoicerr delivers a single‑stack web app that covers the full billing lifecycle.

  • Backend – A NestJS API (backend/src/main.ts) provides REST endpoints for clients, quotes, invoices, receipts, recurring invoices, webhooks, and a plugin system (backend/src/plugins/**). Database access is centralized in backend/src/prisma/prisma.service.ts (Prisma ORM, SQLite/PostgreSQL).
  • Frontend – A React/Vite UI (frontend/src/pages/(app)/invoices/index.tsx, frontend/src/pages/(app)/quotes/index.tsx) consumes the API, offers a drag‑and‑drop form modal (frontend/src/components/form-modal.tsx), and renders PDFs via the backend/src/utils/pdf.ts helpers.
  • Plugins – Pluggable signing (documenso) and storage (local/S3) providers live under backend/src/plugins/signing/** and backend/src/plugins/storage/**, enabling custom token‑based signatures and file uploads without code changes.

How It Is Wired

  1. Entry – backend/src/main.ts calls bootstrap() which creates the NestJS AppModule.
  2. Core hub – backend/src/app.module.ts imports 22 other modules (instability 0.92), making it the primary wiring point.
  3. Database – Most API handlers delegate to PrismaService (findMany, create, update). Example path: bootstrap → AppModule → InvoicesController → InvoicesService → PrismaService.create writes an invoice row (prisma.invoice.create).
  4. Webhooks – WebhookDispatcherService (imported by 15 modules) receives events, looks up Webhook records via Prisma, and forwards to driver classes (discord.driver.ts, slack.driver.ts). The dispatcher’s dispatch method is called 32 times across the codebase.
  5. Plugins – backend/src/plugins/index.ts lazily loads signing/storage plugins via getProviderByType. The Documenso signing flow (documenso.ts) authenticates, requests a signature, and processes a webhook that updates the related quote (handleWebhook → PrismaService.update).
  6. Frontend → Backend – frontend/src/hooks/use-fetch.ts supplies authenticatedFetch, useGet, and useSse. UI pages call useGet (14 callers) to fetch lists, and useSse (8 callers) to stream real‑time invoice status updates.
  7. Hotspots – The most connected module (backend/src/app.module.ts) touches 2 importers and 22 imports, meaning changes there have the widest blast radius. A circular import exists among three settings components (frontend/src/pages/(app)/settings/_components/...), increasing the risk of breakage when refactoring.

How To Use It

# Clone the repo (exact URL required)
git clone https://github.com/moses-y/invoicerr.git
cd invoicerr

# Build & run with Docker (recommended)
docker compose up -d          # uses docker-compose.yml / Dockerfile
# Application will be reachable at http://localhost

# Local development (Node 20+, npm)
# Backend
cd backend
npm ci                         # installs from backend/package-lock.json
npm run start:dev             # starts NestJS server (reads .env.example)

# Frontend
cd ../frontend
npm ci
npm run dev                    # Vite dev server on http://localhost:5173

Required environment variables are documented in backend/.env.example and frontend/.env.example (e.g., DATABASE_URL, JWT_SECRET, SMTP_*). Replace the example values before starting.

Real‑World Use

A freelancer creates a new client via the UI (frontend/src/pages/(app)/clients). The form posts to POST /clients (handled by ClientsController). A quote is drafted, sent to the Documenso signing provider (documenso.ts), and a webhook updates the quote status (handleWebhook). Once signed, the quote is converted to an invoice (InvoicesService.createInvoice), emailed (MailService.sendMail), and payment status is tracked through the Payments module.

Code Health & Issues

  • High – Pin GitHub Actions to commit SHAs (.github/workflows/*).
  • High – Remove committed .env.test and rotate its secrets (backend/.env.test, frontend/.env.test).
  • Medium – Declare least‑privilege GITHUB_TOKEN permissions (.github/workflows/cypress.yml).
  • Medium – Enable Dependabot or Renovate for dependency updates.
  • Medium – Pin Docker base images by digest (Dockerfile).
  • Medium – Add a dependency‑vulnerability scan step in CI.
  • Medium – Add a pre‑commit secret‑leak gate.
  • Medium – Set persist-credentials: false on checkout steps.
  • Medium – Run container as non‑root (USER directive missing).
  • Low – Set timeout-minutes on workflow jobs (.github/workflows/codeql.yml).

Additional static findings: deep nesting (max depth 14) in several TSX and service files, a four‑module import cycle in the settings UI, and duplicated 6‑line code blocks across 74 files.

The Bottom Line

invoicerr offers a complete, Docker‑ready invoicing stack built on familiar React/NestJS/Prisma tech, with a flexible plugin architecture. However, the codebase exhibits high cognitive load, some circular imports, and several security‑hygiene gaps that should be addressed before production deployment. It is best suited for teams comfortable refactoring TypeScript and willing to tighten CI/CD and dependency management.