# How JSON Server's service.ts Implements Core Business Logic: A Deep Dive

> Explore JSON Server's service.ts to understand how it builds a REST API from JSON data, managing CRUD, queries, and relationships. Master its core business logic.

- Repository: [typicode/json-server](https://github.com/typicode/json-server)
- Tags: deep-dive
- Published: 2026-03-01

---

**The `Service` class in [`src/service.ts`](https://github.com/typicode/json-server/blob/main/src/service.ts) acts as the central orchestration layer that transforms lowdb's raw JSON data into a complete REST API, handling CRUD operations, complex querying, and relationship management.**

JSON Server's architecture separates the HTTP layer from data manipulation, placing the core business logic inside a single `Service` class. This class wraps a **lowdb** instance (a lightweight JSON file-based database) and exposes methods that power every REST endpoint. Understanding [`service.ts`](https://github.com/typicode/json-server/blob/main/service.ts) reveals how the project achieves its "zero-configuration" promise while supporting advanced features like embedding, filtering, and referential integrity.

## Service Class Architecture

The `Service` class defined in [`src/service.ts`](https://github.com/typicode/json-server/blob/main/src/service.ts) maintains a private `#db` property that holds the `Low<Data>` instance. This encapsulation ensures all disk I/O happens through a controlled interface. The class exposes three primary responsibility areas: data access validation, query pipeline processing, and state-mutating CRUD operations.

Key helper methods include `#get` (lines 88-90) which retrieves a resource array by name, and `has` (lines 92-94) which validates resource existence before operations proceed. These guards prevent runtime errors when clients request non-existent collections.

## Query Processing Pipeline

The `find` method (lines 110-144) implements the core query engine that transforms raw arrays into paginated, filtered, and sorted results. This method executes a four-stage pipeline:

1.  **Embedding** – The private `embed` helper (lines 23-47) handles relationship expansion via the `_embed` parameter. It detects singular relationships (e.g., `author` mapping to `authors`) by checking for foreign key patterns like `authorId`, and plural relationships by matching child items containing parent IDs (e.g., `postId`).

2.  **Filtering** – The `matchesWhere` function imported from [`src/matches-where.ts`](https://github.com/typicode/json-server/blob/main/src/matches-where.ts) (line 33) evaluates complex `where` clauses against each item, supporting operators defined in [`src/where-operators.ts`](https://github.com/typicode/json-server/blob/main/src/where-operators.ts).

3.  **Sorting** – The `sortOn` utility (lines 35-36) arranges results based on the `sort` parameter, handling both ascending and descending orders.

4.  **Pagination** – The `paginate` function (lines 38-40) slices the final array according to `page` and `perPage` parameters, returning the subset along with total count metadata.

## CRUD Operations and Data Mutation

The `Service` class provides atomic CRUD methods that modify the in-memory state and persist changes via `await this.#db.write()`.

### Create

The `create` method (lines 45-54) generates a unique identifier using [`random-id.ts`](https://github.com/typicode/json-server/blob/main/random-id.ts), merges it into the provided data, pushes the item to the appropriate collection, and immediately writes to disk.

### Update and Patch

Two private helpers handle modifications: `#updateOrPatch` and `#updateOrPatchById` (lines 56-84). These methods distinguish between full replacement (`update`) and partial merging (`patch`). They locate the target item by ID, apply the new data (either replacing or merging), and persist the change.

### Delete with Referential Integrity

The `destroyById` method (lines 102-121) removes an item and maintains database integrity through two mechanisms:

-   **`nullifyForeignKey`** (lines 49-64): After deletion, this helper iterates every collection, identifying foreign keys that reference the deleted ID (e.g., `userId`) and setting them to `null` to prevent dangling pointers.

-   **`deleteDependents`** (lines 66-78): When called with a list of dependent resource names, this function removes child items whose foreign keys were nullified, effectively cascading deletes.

## Implementation Example

The following TypeScript example demonstrates initializing the `Service` class and executing common operations:

```typescript
import { Low } from 'lowdb'
import { JSONFile } from 'lowdb/node'
import { Service } from './service.ts'

// Initialize lowdb with a JSON file
const adapter = new JSONFile<Data>('db.json')
const db = new Low<Data>(adapter)
await db.read()
db.data ||= {}

// Instantiate the service layer
const service = new Service(db)

// Create a new resource
const newPost = await service.create('posts', { 
  title: 'Understanding service.ts', 
  authorId: '1' 
})

// Query with embedding, filtering, and pagination
const results = await service.find('posts', {
  where: { title: { contains: 'service' } },
  sort: 'title',
  page: 1,
  perPage: 10,
  embed: 'author'  // Expands authorId into full author object
})

// Partial update
await service.patchById('posts', newPost.id, { title: 'Updated Title' })

// Delete with cascade to remove orphaned comments
await service.destroyById('posts', newPost.id, ['comments'])

```

## Summary

-   The `Service` class in [`src/service.ts`](https://github.com/typicode/json-server/blob/main/src/service.ts) encapsulates JSON Server's business logic, bridging lowdb's file-based storage with REST API semantics.
-   **Query processing** occurs through a pipeline in `find` (lines 110-144), handling embedding, filtering via `matchesWhere`, sorting, and pagination.
-   **CRUD operations** include `create` (lines 45-54), `#updateOrPatch` (lines 56-84), and `destroyById` (lines 102-121), all persisting via `this.#db.write()`.
-   **Referential integrity** is maintained through `nullifyForeignKey` (lines 49-64) and optional cascading deletes via `deleteDependents` (lines 66-78).
-   Helper utilities `ensureArray` (lines 19-21) and `isItem` (lines 13-15) normalize inputs and validate data shapes.

## Frequently Asked Questions

### What is the role of the Service class in JSON Server?

The `Service` class acts as the core business logic layer that transforms lowdb's raw JSON data into a functional REST API. It handles all data access, query processing (filtering, sorting, pagination), CRUD operations, and relationship management, effectively decoupling the HTTP transport layer from the data manipulation logic.

### How does JSON Server handle relationship embedding in service.ts?

Relationship embedding is handled by the private `embed` helper method (lines 23-47 in [`src/service.ts`](https://github.com/typicode/json-server/blob/main/src/service.ts)). When the `find` method receives an `embed` parameter, it detects whether the relationship is singular (e.g., `author` linked via `authorId`) or plural (e.g., `comments` linked via `postId`), then fetches and attaches the related objects to the response.

### What happens when a resource is deleted in JSON Server?

When `destroyById` is called (lines 102-121), the service removes the item and maintains referential integrity through two mechanisms. First, `nullifyForeignKey` (lines 49-64) scans all collections and sets any foreign key referencing the deleted ID to `null`. Second, if dependent resource names are provided, `deleteDependents` (lines 66-78) removes child items whose foreign keys were nullified, effectively cascading the deletion.

### How does the query pipeline in service.ts process filters and pagination?

The `find` method (lines 110-144) executes a sequential pipeline. It first applies relationship embedding, then passes the dataset to `matchesWhere` (imported from [`src/matches-where.ts`](https://github.com/typicode/json-server/blob/main/src/matches-where.ts)) to evaluate `where` clauses. Next, `sortOn` arranges results by the specified field, and finally `paginate` slices the array according to `page` and `perPage` parameters before returning the final dataset.