Building Products API
Products API: Listing, Search, Pagination, and Create
Welcome! 👋 In this lesson, we’ll turn the “products” backend into a real, usable API endpoint that a frontend (or your Playground UI) can rely on.
You’ll see how Next.js Route Handlers act as thin HTTP “controllers” that parse requests and delegate real work to the service layer. We’ll focus specifically on two behaviors: listing products (with optional search and pagination) and creating a product (with validation and conflict handling). By the end, you’ll be able to trace a request from the route handler → service → repository/database (and back) and understand why each layer has a clear job.
The Products collection endpoint
In REST terms, /api/products represents a collection resource:
- A
GETrequest asks: “Give me a list of products” (optionally filtered and paginated). - A
POSTrequest says: “Create a new product in this collection.”
In this codebase, the route handler is responsible for the HTTP mechanics (reading query params, parsing JSON, returning status codes), while business rules and defaults live in the service layer. That division keeps your API easier to extend later (for example, when you add /api/products/:id endpoints).
Route handler overview: src/app/api/products/route.ts
This file is the entry point for /api/products. It exports functions named after HTTP methods (GET, POST), which Next.js automatically wires up to incoming requests.
This first chunk shows what the route handler depends on. You’ll notice it doesn’t import repositories or SQL—routes talk to services, not directly to the database.
NextRequestis the Next.js request type used in Route Handlers. It gives you the request URL, headers, and body helpers likereq.json().success()anderror()come fromsrc/lib/http/responseand ensure every response follows the same envelope shape (so clients don’t need special-case parsing).listProducts,validateCreateProduct, andcreateProductServicelive insrc/lib/services/productsService.ts. The route’s job is to call these and translate the result into HTTP responses.isPostgresErrorandpgErrorToApiErrorallow the route to treat database-specific errors as structured API errors (instead of returning a generic 500 every time something DB-related goes wrong).
