InvoiceForge is a self-hosted invoice management backend API designed for freelancers and small businesses. It enables users to securely manage clients, generate professional invoices with atomic line items, track payment installments, and automatically derive billing statuses.
This project is built using a clean, layered architecture designed to show engineering maturity, secure data modeling, and robust business logic.
A request passes through clean, structured layers where each component does exactly one job:
flowchart LR
Client["Client (Postman / Frontend)"]
subgraph Server["Node.js + Express API"]
MW["Middleware (parse, log, auth)"]
R["Routers"]
C["Controllers"]
S["Services"]
Repo["Repositories"]
EH["Error handler"]
end
DB[("PostgreSQL")]
FS["File storage"]
Client -->|"HTTP request"| MW --> R --> C --> S --> Repo --> DB
S --> FS
C -.->|"throws error"| EH -.->|"JSON error"| Client
- Router: Maps the URL and HTTP method to the correct controller.
- Controller: Reads client requests, extracts parameters, delegates to a Service, and formats HTTP responses. Contains zero business logic.
- Service: Handles business rules, computations (like invoice totals), and transactional safety. Entirely decoupled from HTTP protocol details.
- Repository: Houses the database queries. The only layer authorized to write SQL queries.
The database schema enforces strict relational constraints and indexing to optimize queries like filtering by invoice status or client listings.
erDiagram
USERS ||--o{ CLIENTS : owns
USERS ||--o{ INVOICES : issues
CLIENTS ||--o{ INVOICES : receives
INVOICES ||--o{ LINE_ITEMS : contains
INVOICES ||--o{ PAYMENTS : records
USERS {
uuid id PK
string email UK
string password_hash
string full_name
string business_name
timestamptz created_at
timestamptz deleted_at
}
CLIENTS {
uuid id PK
uuid user_id FK
string name
string email
string address
timestamptz created_at
timestamptz deleted_at
}
INVOICES {
uuid id PK
uuid user_id FK
uuid client_id FK
string invoice_number
string status
numeric subtotal
numeric tax_rate
numeric total
string currency
date issue_date
date due_date
string logo_url
timestamptz created_at
timestamptz deleted_at
}
LINE_ITEMS {
uuid id PK
uuid invoice_id FK
string description
numeric quantity
numeric unit_price
numeric line_total
}
PAYMENTS {
uuid id PK
uuid invoice_id FK
numeric amount
string method
timestamptz paid_at
}
Here is the progress tracker mapping out what is already completed and what will be implemented next:
- Initialized NPM workspace with ESM (ES Modules) support (
"type": "module"). - Set up initial scripts and installed developer dependencies (
nodemon,dotenv,express). - Built the folder skeleton.
- Configured Git and created
.gitignoreto prevent tracking environment secrets.
- Separated app configuration (
app.js) from server listen execution (server.js) for easier future testing. - Integrated
morganfor developer-friendly request logging. - Created a lightweight
/healthcheck routing system. - Implemented a clean, graceful shutdown sequence listening for
SIGINT/SIGTERMsignals.
- Configured PostgreSQL 16 via Docker Compose.
- Implemented database migrations with
node-pg-migrateto manage structural changes. - Created schemas and primary tables for Users, Clients, Invoices, Line Items, and Payments.
- Configured cascading deletes and index constraints to keep the database fast and clean.
- Created a database connection pooling service (
pool.js) and database seeding script.
- Create typed error classes (
NotFoundError,ValidationError,UnauthorizedError). - Implement central error-handling middleware to avoid leaking database details to the client.
- Create an
asyncHandlerwrapper to forward errors safely without manual try-catch blocks in routes. - Integrate
Zodvalidation middleware for boundary checks.
- Build bcrypt password hashing helpers.
- Set up JSON Web Token (JWT) sign and verify middleware.
- Build
/auth/registerand/auth/loginendpoints. - Create a protected
/auth/meprofile route.
- Implement full CRUD operations for Clients.
- Enforce ownership controls so users can only view/edit clients they own.
- Add pagination, text search (
ILIKE), and sorting with allow-lists.
- Implement multi-table ACID transactions for invoice creation.
- Compute subtotal, tax, and final totals on the server-side only (never trust client input).
- Require schemas checking for negative values, empty lists, and invalid dates.
- Define legal invoice status transitions (e.g.
draft ➡️ sent ➡️ paid). - Implement partial payment support and automatically transition invoice status to
paidoroverdue. - Freeze invoices from modifications once sent.
- Implement safe logo image uploading using
Multer. - Validate uploaded files by magic bytes (content type) instead of extension name only.
- Add
helmetsecurity headers. - Set up rate limiters on authentication paths.
- Perform a full IDOR (Insecure Direct Object Reference) security audit across all routes.
- Set up
vitestorjestand write integration tests withsupertest. - Write unit tests for invoice pricing logic and payment status state transitions.
- Containerize the app using a multi-stage
Dockerfile. - Deploy database to a managed cloud database (Railway/Render/Neon).
- Configure startup migration steps.
- Web Framework: Express.js
- Database: PostgreSQL
- Containerization: Docker Compose
- Logging & Configuration: Morgan, Dotenv
- Database Migrations: Node-pg-migrate