Expense Tracking and Budgeting API

Modular API for managing expenses with JWT-based auth, role-based access, data aggregation, and scheduled summaries.

NestJS TypeORM JWT Swagger PostgreSQL TypeScript

Expense management API built with NestJS and TypeORM. Handles authentication, role-based access, and spending aggregation.

Key Features

  • JWT-based Authentication: Secure user authentication and authorization system
  • Role-based Access Control: Different permission levels for users and administrators
  • Data Aggregation: Intelligent expense categorization and spending analysis
  • Scheduled Summaries: Automated expense reports and budget notifications
  • RESTful API Design: Clean, well-documented API endpoints
  • Swagger Documentation: Interactive API documentation for easy integration

Technical Implementation

graph LR
    Client([Client App]) --> |JWT Auth| API[NestJS API Gateway]
    
    subgraph "Modular Backend"
        API --> Auth[Auth Module]
        API --> Exp[Expense Module]
        API --> Budg[Budget Module]
        API --> Rep[Reporting Module]
    end
    
    Auth --> DB[(PostgreSQL)]
    Exp --> DB
    Budg --> DB
    Rep --> DB

The application follows a modular architecture with clean separation of concerns:

  • Authentication Module: JWT token management and user session handling
  • Expense Module: CRUD operations for expense management
  • Budget Module: Budget creation, tracking, and alerts
  • Reporting Module: Data aggregation and summary generation
  • User Management: Role-based user administration

Technology Stack

  • Backend Framework: NestJS with TypeScript
  • Database: PostgreSQL with TypeORM
  • Authentication: JWT tokens with role-based access
  • Documentation: Swagger/OpenAPI specification
  • Validation: Class-validator for input validation
  • Testing: Jest for unit and integration tests

Architecture Decisions

  • Why NestJS over Express: Module boundaries are enforced at the framework level, so the auth, expense, budget, and reporting domains stay isolated instead of bleeding into a single router file. Dependency injection also keeps test setup small — providers swap in cleanly.
  • Why TypeORM: Entity-first modelling lined up with the domain (User, Expense, Budget, Category) and made schema changes traceable via migrations rather than hand-rolled SQL diffs.
  • JWT over session cookies: API is consumed by multiple clients (mobile, web); stateless tokens removed the need for shared session storage and kept horizontal scaling cheap.
  • Role-based access: Encoded as claims in the JWT and checked with NestJS guards, so authorization lives next to the route definition instead of scattered through controllers.
  • Scheduled summaries: Cron-driven aggregation jobs sit in their own module so the read/write hot path stays uncoupled from reporting workloads.

Trade-offs

  • TypeORM’s metadata-driven model is convenient but leaks abstractions in complex joins — a few reporting queries were dropped to raw SQL where the query builder fought the schema.
  • JWT statelessness means revocation is non-trivial; the current model relies on short-lived tokens rather than a denylist, which is a deliberate simplicity choice.