A backend API for managing workflows and their expenses with authentication, reporting, and budget tracking.
The system allows users to create workflows (projects with budgets), track expenses inside them, and generate reports about spending and remaining budgets.
Expense Workflow API is a RESTful backend service that helps users manage budgets across multiple workflows. Each workflow has a budget and contains multiple expenses. The system tracks spending, calculates remaining budgets, and provides reports both at the user level and per workflow.
Key capabilities:
User authentication using JWT
Workflow management (create, update, delete, archive)
Expense tracking within workflows
Budget usage calculation
Aggregated reporting and category breakdowns
Secure API with middleware protections
The project follows a Clean Architecture / Layered Architecture approach to separate responsibilities and keep the business logic independent from frameworks.
Layers:
Core business models such as User, Workflow, and Expense.
Contains the business logic that coordinates operations such as creating workflows, managing expenses, and generating reports.
Abstractions and implementations responsible for data persistence (PostgreSQL).
Handle HTTP requests, validate inputs, call use cases, and return responses.
Includes database configuration and external dependencies.
Responsible for wiring repositories, use cases, and controllers together.
Separation of concerns
Easier testing
Replaceable infrastructure
Scalable project structure
src
│
├── application
│ ├── expense
│ ├── report
│ ├── user
│ └── workflow
│
├── controller
│ ├── expense
│ ├── report
│ ├── user
│ └── workflow
│
├── repository
│ ├── expense
│ ├── user
│ └── workflow
│
├── entities
│
├── container
│
├── middleware
│
├── config
│
└── app.ts
application → business logic (use cases)
controller → HTTP layer
repository → database interaction
entities → domain models
container → dependency injection setup
middleware → authentication and request middleware
config → database configuration
app.ts → application entry point
Backend
Node.js
Express.js
TypeScript
Database
PostgreSQL
node-postgres (pg)
Authentication
JWT (jsonwebtoken)
bcryptjs
Security
Helmet
CORS
Express Rate Limit
Infrastructure
Docker (PostgreSQL container)
Authentication
POST /register
Register a new user
POST /login
Login user and receive JWT token
Workflow
POST /workflow
Create workflow
GET /workflows
Get all workflows for current user
PUT /workflow/:id
Update workflow
DELETE /workflow/:id
Delete workflow
PATCH /workflow/:id/archive
Archive workflow
PATCH /workflow/:id/unarchive
Unarchive workflow
Expenses
POST /workflow/:workflowId/expense
Create expense in workflow
GET /workflow/:workflowId/expenses
Get all expenses in workflow
PUT /workflow/:workflowId/expense/:id
Update expense
DELETE /workflow/:workflowId/expense/:id
Delete expense
Optional query parameters:
from
to
Example:
/workflow/1/expenses?from=2024-01-01&to=2024-02-01
Reports
GET /reports/overview
Returns:
total workflows
total budget
total expenses
remaining budget
budget usage percentage
category breakdown
GET /workflow/:workflowId/report
Returns workflow-specific statistics:
total expenses
expense count
remaining budget
budget usage percentage
category breakdown
User registers with email and password
Password is hashed using bcrypt
User logs in with credentials
Server generates JWT token
Token is sent in Authorization header
Authorization: Bearer
authMiddleware verifies the token
User ID is extracted and attached to request
Controllers use try/catch blocks and return consistent HTTP responses.
400 Bad Request
Invalid input or validation error
401 Unauthorized
Missing authentication token
403 Forbidden
Invalid or expired token
404 Not Found
Resource does not exist
All errors return JSON:
{
"error": "Error message"
}
Validation happens in multiple layers.
Controllers
Validate request parameters
Validate request body fields
Entities
Enforce domain rules
Validate titles, amounts, budgets, etc.
Use Cases
Ensure business logic rules
Check ownership and relationships
Example validations:
workflowId must be a number
expense amount must be positive
workflow must belong to the authenticated user
The API includes several security measures.
Password Security
Passwords hashed with bcrypt
Authentication
JWT based authentication
Tokens required for protected routes
Rate Limiting
Login and register endpoints protected from brute-force attacks
HTTP Security
Helmet for secure headers
CORS configuration
Data Ownership
Users can only access their own workflows and expenses
Database Protection
Parameterized queries to prevent SQL injection
The project follows a Clean Architecture style to separate domain logic from infrastructure and frameworks.
This improves maintainability, scalability, and testability, but it also increases the number of files and layers in the project.
Repositories abstract database operations from business logic.
This allows the application layer to remain independent of PostgreSQL and makes it easier to switch databases or mock repositories during testing.
Trade-off:
More abstraction layers and interfaces must be maintained.
Controllers are intentionally kept thin.
Their only responsibility is handling HTTP requests and responses.
All business logic lives inside use cases in the application layer.
Benefits:
Easier testing of business logic
Clear separation between transport layer and domain logic
Trade-off:
Slightly more boilerplate code.
The project uses a dedicated container folder to handle Dependency Injection.
The container is responsible for:
Instantiating repositories
Injecting repositories into use cases
Injecting use cases into controllers
Exporting fully wired controllers for routing
Example flow:
Repository → Use Case → Controller
This keeps controllers and use cases decoupled from concrete implementations.
Benefits:
Easier testing with mocked dependencies
Clear dependency graph
Loose coupling between layers
Trade-off:
Requires an additional setup layer
Adds some boilerplate compared to directly instantiating classes inside controllers.
PostgreSQL was chosen because the system relies on relational data and aggregations for reporting (totals, category summaries, budget usage).
Benefits:
Strong relational consistency
Powerful aggregation queries
Reliable transactional support
Trade-off:
Slightly heavier setup compared to embedded databases.
Docker is used to run PostgreSQL locally.
Benefits:
Consistent development environment
Easy setup for new developers
Avoids local database configuration issues
Trade-off:
Requires Docker to be installed.
Install dependencies
npm install
npx tsc
Start backend server
node dist/app.js
Run PostgreSQL using Docker
cd deployment
docker-compose up -d
Run database migration
node dist/database/migrate
The project includes unit tests to verify the correctness of the business logic.
Vitest is used as the testing framework for fast and lightweight unit testing.
Unit tests are implemented for the following parts of the system:
Entities
User entity validation and serialization
Workflow entity validation and behavior
Expense entity validation and serialization
Authentication Use Cases
Register use case
Login use case
Workflow Use Cases
CreateWorkflow
UpdateWorkflow
GetAllWorkflow
DeleteWorkflow
ArchiveWorkflow
UnarchiveWorkflow
Expense Use Cases
CreateExpense
UpdateExpense
GetAllExpenses
DeleteExpense
Reporting Use Cases
ReportOverview
ReportWorkflow
Repositories are mocked during tests so that business logic can be tested independently from the database.
This ensures:
Faster tests
Deterministic results
Isolation of domain logic
To run the test suite:
npm run test
Or with Vitest directly:
npx vitest
The tests validate:
business rules
validation logic
ownership checks
budget calculations
edge cases such as zero budgets or empty datasets