# How the JSON Server Routing System Works: From HTTP Request to CRUD Operation

> Explore the JSON Server routing system. Discover how it maps HTTP requests to CRUD operations using tinyhttp and middleware, delegating data tasks to a Service class.

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

---

**JSON Server's routing system is built on the tinyhttp framework and maps RESTful HTTP endpoints to CRUD operations through a middleware pipeline defined in [`src/app.ts`](https://github.com/typicode/json-server/blob/main/src/app.ts), delegating data operations to a central `Service` class while parsing complex queries via `parseListParams` and `parseWhere`.**

The JSON Server routing system in the typicode/json-server repository provides a zero-configuration REST API layer over a JSON file. Built on tinyhttp rather than Express, it uses a concise middleware stack in [`src/app.ts`](https://github.com/typicode/json-server/blob/main/src/app.ts) to handle static files, CORS, body parsing, and route registration. Every HTTP method maps directly to specific service methods that perform filtering, embedding, and pagination against a lowdb-backed database.

## Architecture and Middleware Pipeline

### The Tinyhttp Foundation

Unlike earlier versions that used Express, the current implementation relies on **tinyhttp**, a lightweight Express-compatible framework. This architectural choice reduces dependencies while maintaining familiar middleware patterns and routing APIs.

### Request Processing Stages

The `createApp` factory in [`src/app.ts`](https://github.com/typicode/json-server/blob/main/src/app.ts) assembles three critical middleware layers before reaching the route handlers:

1. **Static file serving**: The `sirv` middleware serves content from the built-in `public` folder and additional directories specified in `options.static` (lines 98-101).
2. **CORS handling**: A wrapper around `@tinyhttp/cors` dynamically sets `Access-Control-Allow-Headers` and registers a global `OPTIONS *` handler (lines 104-112).
3. **Body parsing**: The `milliparsec` library's `json()` middleware parses JSON bodies for POST, PUT, and PATCH requests (lines 115-117).

## RESTful Route Definitions

After the middleware pipeline, [`src/app.ts`](https://github.com/typicode/json-server/blob/main/src/app.ts) defines route handlers that map HTTP methods to `Service` class operations. All handlers store results in `res.locals['data']` for final serialization by a catch-all middleware (lines 55-63).

### Collection Routes

- **GET** `/:name`: Returns filtered, sorted, and paginated collections. The `parseListParams` function (lines 30-66) extracts reserved query keys including `_sort`, `_page`, `_per_page`, `_embed`, and `_where`, converting them into a structured query object. The `Service.find` method in [`src/service.ts`](https://github.com/typicode/json-server/blob/main/src/service.ts) (lines 10-42) then applies these criteria using `matchesWhere` for filtering.
- **POST** `/:name`: Creates resources. The `withBody` wrapper (lines 68-76) validates that the request body is an object before calling `Service.create` (lines 45-53), which generates a random ID and persists to the lowdb database.

### Single Resource Routes

- **GET** `/:name/:id`: Retrieves individual resources via `Service.findById`. The `_embed` query parameter triggers relational data inclusion through the `embed` helper (lines 96-104).
- **PUT** `/:name/:id`: Full resource replacement using `withIdAndBody` middleware (lines 78-86), delegating to `Service.updateById` (lines 94-100).
- **PATCH** `/:name/:id`: Partial updates handled by `Service.patchById` (lines 92-98).
- **DELETE** `/:name/:id`: Resource removal via `Service.destroyById`, which performs referential integrity cleanup through `nullifyForeignKey` and `deleteDependents` (lines 49-78).

## Query Processing and Filtering

The routing system's filtering capabilities rely on three specialized modules that transform URL queries into executable database operations.

### Parameter Parsing

When handling GET requests to collection routes, `parseListParams` in [`src/app.ts`](https://github.com/typicode/json-server/blob/main/src/app.ts) (lines 30-66) segregates standard query parameters from reserved keywords. It constructs a *where* object by passing query strings to `parseWhere` from [`src/parse-where.ts`](https://github.com/typicode/json-server/blob/main/src/parse-where.ts) (lines 6-28), which supports both modern `field:op=value` syntax and legacy `_lt`, `_gt` operators (lines 58-68).

### Filter Evaluation

The `matchesWhere` function in [`src/matches-where.ts`](https://github.com/typicode/json-server/blob/main/src/matches-where.ts) (lines 24-72) recursively evaluates objects against the parsed criteria. It handles logical `or` operators, comparison operators (`lt`, `gt`, `eq`), set membership (`in`), and string matching (`contains`, `startsWith`, `endsWith`).

### Service Layer Integration

Within [`src/service.ts`](https://github.com/typicode/json-server/blob/main/src/service.ts), the `find` method (lines 26-38) orchestrates query execution: it retrieves the full collection, applies relational embedding via `embed`, filters using `matchesWhere`, applies sorting, and finally paginates results using the `paginate` helper.

## Response Handling

A catch-all middleware at the end of the pipeline inspects `res.locals['data']` to determine the appropriate HTTP status code—returning **404** when no data exists, **201** for successful POST requests, or **200** for other operations—and serializes the payload as JSON.

## Code Examples

### Filtering, Sorting, and Pagination

```typescript
// GET request to retrieve filtered and paginated posts
GET /posts?_where=title:contains=json&page=2&_per_page=5&_sort=createdAt

// Internal execution flow:
// 1. parseListParams extracts: where={title:{contains:"json"}}, page=2, perPage=5, sort="createdAt"
// 2. parseWhere builds the where object from the raw query string
// 3. Service.find filters posts with matchesWhere, sorts on createdAt, then paginates

```

### Creating Resources

```typescript
// POST request to create a new comment
POST /comments
{
  "postId": 3,
  "body": "Nice article!"
}

// Execution flow:
// withBody validates body → Service.create('comments', {postId: 3, body: "Nice article!"})
// Service generates random ID and writes to the lowdb JSON file

```

## Summary

- JSON Server's routing system uses **tinyhttp** as its underlying framework, providing Express-compatible middleware support with lighter dependencies than traditional Express.
- The `createApp` factory in [`src/app.ts`](https://github.com/typicode/json-server/blob/main/src/app.ts) configures static file serving, CORS, and JSON body parsing before registering RESTful routes that handle both collections and individual resources.
- Route handlers delegate all CRUD operations to the `Service` class in [`src/service.ts`](https://github.com/typicode/json-server/blob/main/src/service.ts), which implements business logic for filtering, embedding, pagination, and referential integrity.
- Query parsing occurs in two stages: `parseListParams` extracts pagination and sorting parameters while `parseWhere` converts query strings into structured filter objects supporting operators like `contains`, `gt`, and `in`.
- The `matchesWhere` engine in [`src/matches-where.ts`](https://github.com/typicode/json-server/blob/main/src/matches-where.ts) evaluates complex filter criteria including logical operators and string comparisons against the JSON dataset.
- All responses flow through a final middleware that sets appropriate HTTP status codes (200, 201, 404) and serializes data from `res.locals['data']` as JSON.

## Frequently Asked Questions

### What framework does JSON Server use for routing?

JSON Server uses **tinyhttp**, a lightweight Express-compatible framework, rather than Express itself. This implementation appears in [`src/app.ts`](https://github.com/typicode/json-server/blob/main/src/app.ts) where the `createApp` function assembles the middleware pipeline using tinyhttp-specific imports for CORS (`@tinyhttp/cors`) and body parsing (`milliparsec`).

### How does JSON Server handle complex query parameters like filtering and sorting?

The routing system processes query strings through `parseListParams` in [`src/app.ts`](https://github.com/typicode/json-server/blob/main/src/app.ts) (lines 30-66), which identifies reserved keys like `_sort`, `_page`, and `_where`. For filtering, it delegates to `parseWhere` to convert syntax such as `title:contains=json` into structured objects, then uses `matchesWhere` in [`src/matches-where.ts`](https://github.com/typicode/json-server/blob/main/src/matches-where.ts) to evaluate records against these criteria during `Service.find` execution.

### Where are the RESTful route definitions located in the source code?

All HTTP endpoint definitions reside in [`src/app.ts`](https://github.com/typicode/json-server/blob/main/src/app.ts) within the `createApp` function. This file registers handlers for GET, POST, PUT, PATCH, and DELETE methods on both collection paths (`/:name`) and single resource paths (`/:name/:id`), wrapping business logic calls with validation middleware like `withBody` and `withIdAndBody`.

### How does JSON Server map HTTP methods to database operations?

The routing layer delegates to the `Service` class defined in [`src/service.ts`](https://github.com/typicode/json-server/blob/main/src/service.ts). GET requests trigger `find` (collections) or `findById` (single resources), POST uses `create`, PUT utilizes `updateById`, PATCH calls `patchById`, and DELETE invokes `destroyById`. These methods handle lowdb interactions, random ID generation via `randomId`, and relational cleanup through `nullifyForeignKey` and `deleteDependents`.