Bookshelf is a catalog a reader can search and review: four resources, a handful of endpoints, and every problem a real API has — pagination, validation, authentication, ownership rules, rate limits and documentation.
| Method and path | Does | Auth |
|---|---|---|
| GET /api/books | List books, paginated and filterable | public |
| GET /api/books/:id | One book with its author | public |
| POST /api/books, PATCH /api/books/:id | Add or update a book | editor |
| POST /api/books/:id/reviews | Post a review | any user |
| POST /api/auth/login | Exchange credentials for tokens | public |
Every route reaches data through one object, never through a driver call:
export const bookStore = {
async list({ limit, offset, q }) { /* ... */ },
async get(id) { /* the book, or null */ },
async create(input) { /* the created book */ },
async update(id, patch) { /* the updated book, or null */ }
};Through this chapter the implementation behind that object is an array in memory, which keeps every example runnable with no database installed and the test suite instant. In MongoDB you write a second implementation with the same four methods backed by Mongoose 243,355 models, swap it in data/index.js, and every route, controller and test keeps working untouched; Next.js then calls the running API from Next.js 10,514 server components. Adopt the pattern even where you will never change databases: the moment a route calls the driver directly, your HTTP and persistence layers are welded together and neither can be tested alone.