The problem
I had consumed REST APIs many times as a frontend developer, but never designed the backend side of one from scratch. I wanted to understand, hands-on, how to keep the data access layer decoupled from business logic instead of just reading about it. A simple CRUD API for notes was a small enough domain to focus on the architecture instead of the business rules.
Role and impact
- Individual project, learning / backend fundamentals.
- Learned to apply separation of concerns across controllers, services, and data access.
- Learned the Dependency Inversion Principle (the D in SOLID) by depending on repository interfaces instead of concrete implementations.
- Learned and applied the Repository Pattern to isolate Prisma from business logic.
Technical decisions and challenges
If services call Prisma directly, every business rule ends up tied to a specific ORM, and testing a service means testing a real database client too. I compared calling Prisma directly from the services versus introducing a repository layer between them.
I chose to introduce repository interfaces that services depend on, with Prisma implementations behind them, because it let the service layer depend on an abstraction instead of a concrete data access technology, following the Dependency Inversion Principle.
It would have been faster to put validation, business rules, and database calls all in the controller. I evaluated a single-layer approach against a NestJS module structure with controllers, services, and repositories as distinct layers.
I ended up with controllers that only handle HTTP concerns, services that hold business logic, and repositories that hold data access, because separating those responsibilities made each layer independently testable and easier to reason about as the API grew past basic CRUD.
Detailed stack
| Layer | Technology | Why |
|---|---|---|
| API framework | NestJS, TypeScript | Built-in dependency injection, ideal for practicing DIP |
| Database | PostgreSQL, Neon | Managed Postgres for a real relational data access layer |
| ORM | Prisma | Type-safe queries, isolated behind the repository layer |
| Testing | Jest | Unit tests for services against mocked repository interfaces |
| Docs | Swagger | Auto-generated, explorable API documentation |
