A production-oriented envelope budgeting application built with Node.js, Express, PostgreSQL, and Sequelize ORM, paired with a vanilla JavaScript frontend styled after the Linear.app design system.
Repository: github.com/Mahnoor-Zaffar/Envelope-Budget-API
| Resource | URL |
|---|---|
| App | https://envelope-budget-api.onrender.com |
| Health | https://envelope-budget-api.onrender.com/health |
| Swagger | https://envelope-budget-api.onrender.com/api-docs |
Free-tier services sleep when idle — first load may take ~30 seconds.
- Overview
- Project Status
- Tech Stack
- Architecture
- Data Model
- Project Structure
- Getting Started
- API Reference
- Interactive Documentation
- Frontend
- Deployment
- Design System
- Related Documentation
- License
Envelope budgeting allocates income into category-specific envelopes (groceries, rent, entertainment, etc.). Each envelope tracks an allocated budget and a spendable balance. When an envelope is depleted, spending in that category stops until funds are reallocated or new income is logged.
This project evolved from an in-memory Express prototype (Part I) into a persistent, database-backed API (Part II) with:
- Full envelope CRUD and atomic fund transfers
- A dedicated transaction subsystem that logs external expenditures and adjusts envelope balances
- Swagger UI for interactive API exploration
- Render-ready deployment configuration
| Phase | Scope | Status |
|---|---|---|
| Phase 1 | Local PostgreSQL setup, .env configuration, database creation |
✅ Complete |
| Phase 2 | Sequelize models, envelope + transaction API, Swagger integration | ✅ Complete |
| Phase 3 | Local verification (health check, Swagger, curl/API testing) | ✅ Complete |
| Phase 4 | Frontend migration to /transactions API |
✅ Complete |
| Phase 5 | Production deployment on Render | ✅ Complete |
| Phase 6 | Part III — income/funding, transaction UI parity, integration tests | ✅ Complete |
See todo.md for the live task board.
| Layer | Technology |
|---|---|
| Runtime | Node.js ≥ 18 |
| Web framework | Express 4 |
| Database | PostgreSQL |
| ORM | Sequelize 6 |
| API docs | Swagger UI (swagger-ui-express) |
| Security | Helmet, CORS, in-memory rate limiting |
| Frontend | Vanilla HTML / CSS / JavaScript (zero build step) |
| Deployment target | Render (Web Service + Managed PostgreSQL) |
┌─────────────────────────────────────────────────────────────────────┐
│ Browser Client │
│ public/index.html · styles.css · app.js │
│ │ fetch() │
└──────────────────────────────┼──────────────────────────────────────┘
│ HTTP / JSON
┌──────────────────────────────┼──────────────────────────────────────┐
│ Express Server │
│ ┌──────────┐ ┌─────────────────────────────────────────────┐ │
│ │ server.js│──►│ Middleware: Helmet · CORS · Rate Limit · JSON│ │
│ └──────────┘ └─────────────────────────────────────────────┘ │
│ │ │
│ ├── /api-docs ──────► Swagger UI (docs/swagger.json) │
│ ├── /health ────────► Health check │
│ ├── /envelopes ─────► envelopeRoutes → envelopeController │
│ └── /transactions ──► transactionRoutes → transactionController│
│ │ │
│ ┌───────────▼───────────┐ │
│ │ Sequelize ORM │ │
│ │ Envelope · Transaction│ │
│ └───────────┬───────────┘ │
└──────────────────────────────────────────┼───────────────────────────┘
│ SQL (pooled connection)
┌────────────▼────────────┐
│ PostgreSQL │
│ envelopes · transactions│
└─────────────────────────┘
- Layered MVC — routes, controllers, and models are decoupled; controllers own validation, models own persistence.
- Database transactions — fund transfers and transaction writes use
sequelize.transaction()with row-level locks to preserve atomicity. - Defensive validation — every request is validated at the controller layer before hitting the database.
- Uniform API contract —
{ data: ... }on success,{ error: "..." }on failure. - Environment-driven config — connection strings and pool settings resolve from environment variables; SSL is enabled automatically in production.
[ Envelope ] 1 ──── * [ Transaction ]
| Column | Type | Constraints |
|---|---|---|
id |
Integer | Primary key, auto-increment |
title |
String(128) | Required, unique |
budget |
Decimal(12,2) | Required, ≥ 0 |
balance |
Decimal(12,2) | Required, ≥ 0 |
| Column | Type | Constraints |
|---|---|---|
id |
Integer | Primary key, auto-increment |
date |
Timestamp | Required |
amount |
Decimal(12,2) | Required, > 0 |
recipient |
String(256) | Required |
envelopeId |
Integer | Foreign key → envelopes.id (CASCADE on delete) |
Domain rules:
- Creating a transaction deducts
amountfrom the linked envelope's balance. - Deleting a transaction refunds
amountback to the envelope. - Updating a transaction recalculates balances safely when
amountorenvelopeIdchanges. - Envelope balances cannot drop below zero.
personal-budget-expressjs/
├── config/
│ ├── constants.js # Ports, paths, status codes, error messages
│ └── database.js # Sequelize connection pool + SSL config
├── controllers/
│ ├── envelopeController.js # Envelope CRUD + transfer handlers
│ └── transactionController.js
├── models/
│ ├── index.js # Associations + initDatabase()
│ ├── envelope.js
│ └── transaction.js
├── routes/
│ ├── envelopeRoutes.js
│ └── transactionRoutes.js
├── utils/
│ └── controllerHelpers.js # Parsing, formatting, error mapping
├── docs/
│ └── swagger.json # OpenAPI 3.0 specification
├── public/ # Vanilla frontend (Phase 4 migration in progress)
├── server.js # Express entry point
├── .env.example # Environment variable template
├── render.yaml # Render Blueprint (IaC)
├── DEPLOYMENT.md # Production deployment guide
├── PRD.md # Product requirements (Part II)
├── todo.md # Kanban task board
└── README.md
- Node.js ≥ 18
- npm ≥ 8
- PostgreSQL ≥ 14 (local install or Docker)
git clone https://github.com/Mahnoor-Zaffar/Envelope-Budget-API.git
cd Envelope-Budget-API
npm installcp .env.example .envEdit .env with your local PostgreSQL credentials:
PORT=3000
NODE_ENV=development
DATABASE_URL=postgresql://YOUR_USER@localhost:5432/envelope_budget
.envis gitignored and never committed. Only.env.exampleis tracked.
createdb envelope_budget
# or: psql postgres -c "CREATE DATABASE envelope_budget;"Ensure PostgreSQL is running:
pg_isready# Development (auto-restart on file changes)
npm run dev
# Production
npm startOn successful startup:
✓ PostgreSQL connected and models synchronized.
✦ Envelope Budget API listening on http://localhost:3000
Envelopes: /envelopes
Transactions: /transactions
Swagger: /api-docs
Health: /health
| Variable | Default | Description |
|---|---|---|
PORT |
3000 |
HTTP port |
NODE_ENV |
development |
Set to production on Render (enables DB SSL) |
DATABASE_URL |
— | Required. PostgreSQL connection string |
DB_POOL_MAX |
5 |
Max connections in pool |
DB_POOL_MIN |
0 |
Min idle connections |
DB_LOGGING |
false |
Set to true to log SQL queries |
All endpoints accept and return JSON. Base URLs:
| Resource | Base path |
|---|---|
| Envelopes | /envelopes |
| Transactions | /transactions |
| Health | /health |
| Docs | /api-docs |
| Method | Endpoint | Description |
|---|---|---|
POST |
/envelopes |
Create envelope (title, budget) |
GET |
/envelopes |
List envelopes (?page=&limit=) + aggregated totalBudget |
GET |
/envelopes/:id |
Get envelope by ID |
PUT |
/envelopes/:id |
Update title, budget, and/or balance |
DELETE |
/envelopes/:id |
Delete envelope (cascades transactions) |
POST |
/envelopes/transfer/:fromId/:toId |
Atomic fund transfer (amount) |
POST |
/envelopes/distribute |
Distribute income proportionally by budget allocation |
POST |
/envelopes/:id/fund |
Add funds to a single envelope balance |
Create envelope example:
curl -X POST http://localhost:3000/envelopes \
-H "Content-Type: application/json" \
-d '{"title":"Groceries","budget":500}'Transfer example:
curl -X POST http://localhost:3000/envelopes/transfer/1/2 \
-H "Content-Type: application/json" \
-d '{"amount":50}'| Method | Endpoint | Description |
|---|---|---|
POST |
/transactions |
Log expenditure; deducts from envelope balance |
GET |
/transactions |
List transactions (?page=&limit=) |
GET |
/transactions/:id |
Get transaction by ID |
PUT |
/transactions/:id |
Update transaction; recalculates balances |
DELETE |
/transactions/:id |
Delete transaction; refunds envelope balance |
| Method | Endpoint | Description |
|---|---|---|
GET |
/reports/monthly?year=&month= |
Monthly spend summary by envelope |
| Method | Endpoint | Description |
|---|---|---|
POST |
/auth/register |
Create account (email, password) → JWT |
POST |
/auth/login |
Login → JWT |
GET |
/auth/me |
Current user (requires Authorization: Bearer) |
Create transaction example:
curl -X POST http://localhost:3000/transactions \
-H "Content-Type: application/json" \
-d '{
"date": "2026-06-25T12:00:00.000Z",
"amount": 42.50,
"recipient": "Whole Foods",
"envelopeId": 1
}'{ "error": "Descriptive error message." }| Status | Meaning |
|---|---|
400 |
Validation failure, insufficient funds, overdraft |
404 |
Envelope or transaction not found |
429 |
Rate limit exceeded |
500 |
Unexpected server error |
Full OpenAPI 3.0 specs with request/response schemas are served at:
http://localhost:3000/api-docs
Use Swagger UI to explore all endpoints, payload shapes, and status code variations without leaving the browser.
The client lives in public/ and is served as static assets by Express. It provides:
- Sidebar navigation with Overview, Envelopes, Activity, and New & Transfer views
- Data-dense envelope table with monospace balances, progress bars, and Add funds action
- Transaction ledger with edit/delete, payee, date, amount, and category micro-badges
- Distribute income across envelopes proportionally by budget allocation
- Create, edit, delete, transfer, and spend flows via flat modals and border-aligned inputs
- Toast notifications and keyboard-accessible modal dialogs
- Linear.app-inspired dark theme with lavender accent (
#5e6ad2)
| Feature | Endpoint |
|---|---|
| Record spending | POST /transactions |
| Edit / delete transaction | PUT / DELETE /transactions/:id |
| View history | GET /transactions |
| Distribute income | POST /envelopes/distribute |
| Add funds to envelope | POST /envelopes/:id/fund |
| Envelope CRUD + transfer | /envelopes |
Integration tests use Node.js built-in node:test and supertest. Tests run against a separate database ({your_db}_test by default, or set TEST_DATABASE_URL).
# Create a test database (once)
createdb envelope_budget_test
# Run the suite
npm testCoverage includes envelope CRUD, transfers, income distribution, fund top-ups, transaction balance deduction/refund, and insufficient-funds rejection.
Production deployment targets Render with a managed PostgreSQL instance.
Live application: https://envelope-budget-api.onrender.com
| Resource | URL |
|---|---|
| Frontend | https://envelope-budget-api.onrender.com |
| Health check | https://envelope-budget-api.onrender.com/health |
| Swagger UI | https://envelope-budget-api.onrender.com/api-docs |
- Quick start: use the included
render.yamlBlueprint - Step-by-step guide: see
DEPLOYMENT.md
Render injects DATABASE_URL automatically when the database is linked to the web service. Set NODE_ENV=production to enable SSL for database connections.
| Limit | Impact |
|---|---|
| Web service sleep | ~30s cold start after inactivity |
| Free Postgres expires in 30 days | Export or upgrade before data loss |
| One free DB per account | Blueprint/manual setup must reuse or upgrade |
See DEPLOYMENT.md for redeploy steps and custom domain setup.
The UI follows the Linear.app aesthetic. Full visual specs are in linear.app/DESIGN.md.
| Token | Value | Usage |
|---|---|---|
| Canvas | #010102 |
Page background |
| Surface 1 | #0f1011 |
Panels and cards |
| Hairline | #23252a |
Borders and dividers |
| Ink | #f7f8f8 |
Primary text |
| Primary | #5e6ad2 |
CTAs, focus rings, active states |
| Success | #27a644 |
Positive balances |
Typography uses Inter for UI and JetBrains Mono for numeric values. Spacing follows a 4px grid. Depth is carried by surface lift and hairline borders — not drop shadows.
| Document | Purpose |
|---|---|
DOCS.md |
Documentation index — all PRDs and guides |
PRD.md |
Product requirements — Part II scope and acceptance criteria |
PRD-PART-III.md |
Part III scope — income, UI parity, tests |
PRD-PART-IV.md |
Part IV — auth, pagination, reports, CI |
todo.md |
Kanban board — current task status |
DEPLOYMENT.md |
Render deployment, redeploy, custom domain |
docs/swagger.json |
OpenAPI 3.0 specification |
linear.app/DESIGN.md |
Linear.app design system reference |
CONTRIBUTING.md |
Commit format and local checks |
LICENSE |
MIT license |
This project is licensed under the MIT License.

