data/index.js holds no logic. It is the contract, written as a comment and one export, and the only file MongoDB edits when the records move into MongoDB 1,815 .
// The store contract. Every method is async and returns plain objects.
// books.list({ limit, offset, q, genre, authorId, sort }) -> { total, items }
// books.get(id) / create(input) / update(id, patch) -> book|null remove(id) -> boolean
// authors.list({ limit, offset }) authors.get(id) users.findByEmail(email)
// reviews.listForBook(bookId, { limit, offset }) get(id) / create(input) / remove(id)
// reviews.findByUserAndBook(userId, bookId) -> review|null
// Chapter 4 adds createMongoStore() with these methods and changes only the line below.
import { createMemoryStore } from './memory.js';
export const createStore = createMemoryStore;Four properties make the swap possible. It returns plain objects: a service calling book.save() is welded to Mongoose 243,355 , which is why MongoDB ends its reads with .lean(). It is async throughout, even in memory where nothing awaits, so a network round trip changes no caller. Collection reads return { total, items }, because offset pagination needs a count. And createStore() is a factory, so each test gets a clean catalog with no teardown.
export function createMemoryStore() {
const books = structuredClone(seedBooks); // per-instance copies, not the seeds
let nextBook = books.length + 1;
return { kind: 'memory', books: {
async list({ limit, offset, q, genre, authorId, sort }) {
let rows = books;
if (q) rows = rows.filter((b) => b.title.toLowerCase().includes(q.toLowerCase()));
if (genre) rows = rows.filter((b) => b.genre === genre);
if (authorId) rows = rows.filter((b) => b.authorId === authorId);
const dir = sort?.startsWith('-') ? -1 : 1, key = sort?.replace(/^-/, '') ?? 'title';
rows = [...rows].sort((a, b) => (a[key] > b[key] ? dir : a[key] < b[key] ? -dir : 0));
return { total: rows.length, items: clone(rows.slice(offset, offset + limit)) };
},
async get(id) { return clone(books.find((b) => b.id === id) ?? null); }
} }; // then create, update, remove, and the authors, reviews and users groups
}The clone helper — structuredClone guarded for null — stops a class of phantom bug. Without it books.get('bk_1') hands out a live reference into the array, a service that mutates what it received writes through to the store, and tests start depending on each other.