LedgerGuard is a backend engineering assignment for a finance data processing and access control system. The project is intentionally scoped for assessment quality: correct RBAC, clean module boundaries, predictable APIs, and a small stack that is easy to explain.
Tech Stack
- TypeScript
- NestJS
- Prisma
- SQLite
- JWT authentication
- Swagger
class-validator
Architecture Overview
The API is split into four business modules plus shared infrastructure:
AuthModule: seeded-user login and JWT issuanceUsersModule: admin-only user, role, and status managementFinancialRecordsModule: record CRUD and filteringDashboardModule: summary, trends, and recent activity- Shared infrastructure: Prisma service, JWT/role guards, validation, and Prisma error mapping
This keeps responsibilities clear without overengineering the project.
Role Matrix
| Capability | Viewer | Analyst | Admin |
|---|---|---|---|
| Login | Yes | Yes | Yes |
| Login if inactive | No | No | No |
| Read dashboard summary/trends/activity | Yes | Yes | Yes |
| Read financial records | No | Yes | Yes |
| Create/update/delete financial records | No | No | Yes |
| Manage users, roles, and active status | No | No | Yes |
Core Entities
User
idemailpasswordHashfullNameroleisActivecreatedAtupdatedAt
FinancialRecord
idtype(incomeorexpense)categoryamountdescriptiontransactionDatecreatedByIdcreatedAtupdatedAt
API Summary
Auth
POST /api/auth/login
Users
GET /api/usersGET /api/users/:idPOST /api/usersPATCH /api/users/:idDELETE /api/users/:id
Financial Records
GET /api/financial-recordsGET /api/financial-records/:idPOST /api/financial-recordsPATCH /api/financial-records/:idDELETE /api/financial-records/:id
Supported filters:
typecategorystartDateendDate
Dashboard
GET /api/dashboard/summaryGET /api/dashboard/monthly-trendsGET /api/dashboard/recent-activity
Dashboard query parameters:
startDateendDatelimiton recent activity only
Sample Credentials
- Admin:
admin@ledgerguard.local/Admin123! - Analyst:
analyst@ledgerguard.local/Analyst123! - Viewer:
viewer@ledgerguard.local/Viewer123! - Inactive user:
inactive.viewer@ledgerguard.local/Viewer123!
Setup
- Install dependencies.
- Create the environment file.
Copy-Item .env.example .env- Generate the Prisma client.
- Run migrations and seed the database.
npm run prisma:migrate -- --name init
If migrations already exist and you only want fresh seed data:
- Start the API.
- Open Swagger.
http://localhost:3000/docs
Scripts
npm run start:dev: start the app in developmentnpm run build: compile TypeScriptnpm test: run unit and integration testsnpm run prisma:generate: generate Prisma clientnpm run prisma:migrate -- --name <name>: create and apply a migrationnpm run prisma:seed: seed sample data
Sample API Usage
1. Login
curl -X POST http://localhost:3000/api/auth/login ^ -H "Content-Type: application/json" ^ -d "{\"email\":\"admin@ledgerguard.local\",\"password\":\"Admin123!\"}"
Response:
{
"accessToken": "JWT_TOKEN",
"user": {
"id": 1,
"email": "admin@ledgerguard.local",
"fullName": "System Admin",
"role": "admin",
"isActive": true
}
}2. List financial records as analyst
curl "http://localhost:3000/api/financial-records?type=income&startDate=2026-01-01T00:00:00.000Z&endDate=2026-03-31T23:59:59.999Z" ^ -H "Authorization: Bearer JWT_TOKEN"
3. Create a financial record as admin
curl -X POST http://localhost:3000/api/financial-records ^ -H "Authorization: Bearer JWT_TOKEN" ^ -H "Content-Type: application/json" ^ -d "{\"type\":\"income\",\"category\":\"bonus\",\"amount\":250.50,\"description\":\"Quarterly bonus\",\"transactionDate\":\"2026-03-29T10:00:00.000Z\"}"
4. Get dashboard summary as viewer
curl "http://localhost:3000/api/dashboard/summary?startDate=2026-01-01T00:00:00.000Z&endDate=2026-03-31T23:59:59.999Z" ^ -H "Authorization: Bearer JWT_TOKEN"
5. Deactivate a user as admin
curl -X PATCH http://localhost:3000/api/users/3 ^ -H "Authorization: Bearer JWT_TOKEN" ^ -H "Content-Type: application/json" ^ -d "{\"isActive\":false}"
Additional Verification
As a final validation step, I also exercised the backend APIs using my own API testing tool, ReqFlow. I used it as an additional verification layer alongside the automated test suite to confirm the seeded login flows and role-based behavior manually.
API verification screenshots for the submission are available in:
Validation and Error Handling
- Global DTO validation with whitelist and rejection of unknown fields
- Standard Nest status codes for auth, access control, validation, and missing resources
- Prisma exception filter for useful conflict and not-found responses
- Inactive users are blocked both at login and on protected routes
- Invalid date ranges are rejected with
400 Bad Request - Admin cannot delete, deactivate, or demote their own account
Assumptions and Tradeoffs
- Seeded login is sufficient; there is no signup or refresh token flow
- SQLite is used for simplicity and local setup speed
- Amounts are stored as decimals and returned as strings to avoid float ambiguity
- Pagination is intentionally omitted because it is not part of the assignment brief
- Dashboard values are computed from records directly instead of maintaining derived tables
Short Project Walkthrough
AuthModuleissues JWTs for seeded users.UsersModulelets admin manage users, roles, and active status.FinancialRecordsModuleprovides CRUD plus filters by type, category, and date range.DashboardModuleexposes read-only derived views for totals, trends, and recent activity.- Shared guards enforce JWT auth and role checks across all protected routes.