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.
- TypeScript
- NestJS
- Prisma
- SQLite
- JWT authentication
- Swagger
class-validator
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.
| 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 |
idemailpasswordHashfullNameroleisActivecreatedAtupdatedAt
idtype(incomeorexpense)categoryamountdescriptiontransactionDatecreatedByIdcreatedAtupdatedAt
POST /api/auth/login
GET /api/usersGET /api/users/:idPOST /api/usersPATCH /api/users/:idDELETE /api/users/:id
GET /api/financial-recordsGET /api/financial-records/:idPOST /api/financial-recordsPATCH /api/financial-records/:idDELETE /api/financial-records/:id
Supported filters:
typecategorystartDateendDate
GET /api/dashboard/summaryGET /api/dashboard/monthly-trendsGET /api/dashboard/recent-activity
Dashboard query parameters:
startDateendDatelimiton recent activity only
- Admin:
admin@ledgerguard.local/Admin123! - Analyst:
analyst@ledgerguard.local/Analyst123! - Viewer:
viewer@ledgerguard.local/Viewer123! - Inactive user:
inactive.viewer@ledgerguard.local/Viewer123!
- Install dependencies.
npm install- Create the environment file.
Copy-Item .env.example .env- Generate the Prisma client.
npm run prisma:generate- Run migrations and seed the database.
npm run prisma:migrate -- --name initIf migrations already exist and you only want fresh seed data:
npm run prisma:seed- Start the API.
npm run start:dev- Open Swagger.
http://localhost:3000/docs
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
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
}
}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"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\"}"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"curl -X PATCH http://localhost:3000/api/users/3 ^
-H "Authorization: Bearer JWT_TOKEN" ^
-H "Content-Type: application/json" ^
-d "{\"isActive\":false}"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:
docs/screenshots/
- 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
- 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
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.