Simple finance/transactions API built with TypeScript + Express + PostgreSQL.
- Auth
- Register user (email + password)
- Login to receive a JWT
- Protected routes via
Authorization: Bearer <token>
- Transactions
- Add a transaction
- List transactions with filters (date range, category, sorting, pagination)
- Delete a transaction
- Summary + category + daily aggregations
- Runtime: Node.js
- API: Express
- DB: PostgreSQL
- Language: TypeScript (compiled to CommonJS output in
out/)
- Node.js: 20+ recommended
- PostgreSQL: 15+ recommended
- Or use Docker + Docker Compose (recommended for easiest setup)
The app reads environment variables at runtime (see src/config/env.config.ts).
Required:
PORT: default"3000"DB_HOSTDB_PORT(number, e.g.5432)DB_NAMEDB_USERDB_PASSWORDJWT_SECRET(required for login + auth middleware)
Create a file named .env in the repo root:
PORT=3000
DB_HOST=localhost
DB_PORT=5432
DB_NAME=finance
DB_USER=postgres
DB_PASSWORD=postgres
JWT_SECRET=change_me_in_productioncreatedb financeOr via psql:
psql -U postgres -c "CREATE DATABASE finance;"The schema uses gen_random_uuid() for primary keys, which requires the pgcrypto extension.
psql -U postgres -d finance -c "CREATE EXTENSION IF NOT EXISTS pgcrypto;"Schema file: src/database/schema.sql
psql -U postgres -d finance -f src/database/schema.sqluserstransactions(referencesusers(id)withON DELETE CASCADE)
npm installRuns TypeScript directly with tsx:
npm run devServer will listen on http://localhost:3000 by default.
npm run build
npm startThis runs node out/index.js.
This repo includes Dockerfile + docker-compose.yml for the API + Postgres.
docker compose up --build- API:
http://localhost:3000 - Postgres:
localhost:5432
docker-compose.yml sets DB variables, but you must also provide JWT_SECRET for auth/login to work.
Options:
- Option A (quick): export before running compose
export JWT_SECRET=change_me
docker compose up --build- Option B (recommended): add it to
docker-compose.ymlunderservices.app.environment
JWT_SECRET: change_meThe Postgres image does not auto-run this repo’s schema by default, so run it once:
docker exec -i finance_db psql -U postgres -d finance -c "CREATE EXTENSION IF NOT EXISTS pgcrypto;"
docker exec -i finance_db psql -U postgres -d finance < src/database/schema.sqlAfter this, the API endpoints can read/write data.
docker compose downTo also remove the database container data (destructive):
docker compose down -vBase URL: http://localhost:3000
Body:
{ "email": "test@example.com", "password": "password123" }Body:
{ "email": "test@example.com", "password": "password123" }Response includes:
login_token: JWT string
Header:
Authorization: Bearer <token>Header:
Authorization: Bearer <token>All transaction routes require:
Authorization: Bearer <token>Body:
{ "category": "food", "amount": 12.5, "note": "lunch" }Query params supported (all optional):
start: default2000-01-01(YYYY-MM-DD)end: default2100-01-01(YYYY-MM-DD)category: filter by categorysortBy: one ofdate | category | amount(defaults todate)sortOrder:ASCorDESC(defaults toDESC)limit: default10offset: default0
Example:
curl -H "Authorization: Bearer $TOKEN" \
"http://localhost:3000/api/transaction?start=2026-01-01&end=2026-12-31&sortBy=amount&sortOrder=DESC&limit=50&offset=0"Deletes a transaction by id:
curl -X DELETE -H "Authorization: Bearer $TOKEN" \
"http://localhost:3000/api/transaction/<transaction_uuid>"Returns totals (sum/count/month-to-date).
Returns totals grouped by category.
Returns totals grouped by date (YYYY-MM-DD).
Enable pgcrypto in your database:
psql -U postgres -d finance -c "CREATE EXTENSION IF NOT EXISTS pgcrypto;"Make sure JWT_SECRET is set in your environment (local) or container environment (Docker).
Verify these match your Postgres instance:
DB_HOST,DB_PORT,DB_NAME,DB_USER,DB_PASSWORD
For Docker Compose, DB_HOST should be the service name db (this is already set in docker-compose.yml).