# Performance Considerations for JSON Server with Large JSON Databases

> Discover performance issues with JSON Server and large databases. Understand memory pressure and latency with O(N) scans and full rewrites for datasets over thousands of records.

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

---

**JSON Server loads the entire JSON file into memory via lowdb, resulting in linear O(N) query scans and full-file rewrites on every mutation, which causes significant memory pressure and write latency as databases grow beyond tens of thousands of records.**

When prototyping REST APIs with `typicode/json-server`, developers often start with small JSON files but eventually face slowdowns as data scales. Understanding the **performance considerations for JSON Server with large JSON databases** is critical before deploying to production or stress-testing your mock API.

## How JSON Server Handles Large Databases In-Memory

### Memory Architecture and the Lowdb Adapter

In [`src/bin.ts`](https://github.com/typicode/json-server/blob/main/src/bin.ts) (lines 28-38), JSON Server initializes a `Low` instance from lowdb with a `NormalizedAdapter` wrapping the file observer. This design loads the entire parsed JSON object into the Node.js heap immediately upon startup. The `db.data` object (referenced in [`src/service.ts`](https://github.com/typicode/json-server/blob/main/src/service.ts) line 81) holds all collections in memory for the process lifetime.

For a 200 MB JSON file containing approximately 2 million records, this consumes roughly 400 MB of heap space. When the file size approaches or exceeds the default Node.js memory limit (1.5 GB), the process risks `HEAP OUT OF MEMORY` crashes during garbage collection cycles.

### Write Amplification on Every Mutation

Every `POST`, `PUT`, `PATCH`, or `DELETE` operation triggers a complete file rewrite. In [`src/adapters/normalized-adapter.ts`](https://github.com/typicode/json-server/blob/main/src/adapters/normalized-adapter.ts) (lines 44-46), the adapter's `write` method serializes the entire in-memory database back to disk, including the `$schema` metadata.

This write amplification means that modifying a single 1 KB record in a 100 MB database requires rewriting the entire 100 MB file. As database size grows, write latency increases linearly, potentially blocking the event loop for seconds during large file serializations.

## Query Performance Bottlenecks in Large Collections

### Linear Scanning with matchesWhere()

The `Service.find()` method in [`src/service.ts`](https://github.com/typicode/json-server/blob/main/src/service.ts) (lines 20-42) implements filtering by calling `matchesWhere()` from [`src/matches-where.ts`](https://github.com/typicode/json-server/blob/main/src/matches-where.ts) (lines 24-88). This function performs a linear O(N) scan over every item in the collection, checking each record against the query predicates.

Complex `_where` clauses involving operators like `_gt`, `_lt`, or `_contains` execute multiple comparisons per record. Without database indexes, querying a collection of 100,000 items requires evaluating 100,000 predicate checks, consuming significant CPU cycles per request.

### The Cost of Embedding and Sorting

The `_embed` parameter triggers nested iteration across related collections. For each result item, the embed logic walks every related collection to find matches, creating O(N × M) complexity where N is the result set size and M is the related collection size.

Sorting compounds the overhead. The `sortOn()` utility (invoked in [`src/service.ts`](https://github.com/typicode/json-server/blob/main/src/service.ts)) sorts the entire filtered result set in memory using JavaScript's native sort algorithm. Sorting millions of objects creates substantial memory pressure and CPU load, particularly when sorting on non-indexed string fields.

### Pagination Overhead

While pagination in [`src/paginate.ts`](https://github.com/typicode/json-server/blob/main/src/paginate.ts) (lines 11-38) limits the response payload using `Array.prototype.slice()`, the preceding filter and sort operations must process the entire collection first. Pagination reduces network overhead but does not mitigate the linear scan costs incurred before the slice operation executes.

## Benchmarking JSON Server with Large Files

The following examples demonstrate real-world performance characteristics when JSON Server handles large datasets.

Loading a 200 MB JSON file containing approximately 2 million records:

```javascript
import { readFileSync } from 'fs';
import { Low } from 'lowdb';
import { NormalizedAdapter } from './src/adapters/normalized-adapter.js';
import { JSONFile } from 'lowdb/node';

const adapter = new JSONFile('big-db.json');
const db = new Low(new NormalizedAdapter(adapter));

console.time('load');
await db.read();
console.timeEnd('load');
// Output: load: 2345.123ms
// Heap usage: ~400 MB

```

Querying with pagination against the full dataset:

```bash

# Request 10 items from a collection of 2M records

curl "http://localhost:3000/posts?_page=1&_per_page=10"

```

```javascript
// Server-side processing in src/service.ts
// matchesWhere() iterates 2M items → 300ms-1s latency

```

Write operation latency:

```bash

# POST to a 200MB database

curl -X POST -H "Content-Type: application/json" \
  -d '{"title":"new post"}' http://localhost:3000/posts

```

> **Observed behavior:** The request blocks for approximately 1-2 seconds while `NormalizedAdapter.write` serializes the entire 200 MB file back to disk.

## Mitigation Strategies for Large Databases

When working with large datasets in JSON Server, apply these optimization techniques to minimize performance degradation.

**Limit Result Size with Pagination**

Always use `_page` and `_per_page` parameters to reduce response payload. While this does not eliminate the linear scan cost, it minimizes network overhead and client-side processing.

**Avoid Heavy Embedding**

Replace `_embed` parameters with separate requests. Fetch primary resources first, then retrieve related data by ID to avoid O(N × M) nested iteration costs.

**Simplify Where Clauses**

Use simple equality checks (`field=value`) rather than complex operators (`_gt`, `_lt`, `_contains`). The `matchesWhere()` function in [`src/matches-where.ts`](https://github.com/typicode/json-server/blob/main/src/matches-where.ts) performs fewer comparisons per record with simple predicates.

**Split the Database**

Store independent collections in separate JSON files and run multiple JSON Server instances. This reduces per-process memory footprint and write amplification costs.

**Increase Node Heap Size**

As a temporary measure, increase the memory limit:

```bash
node --max-old-space-size=4096 ./node_modules/.bin/json-server --watch db.json

```

This delays out-of-memory errors but does not address linear scan inefficiency.

**Switch to a Real Database**

For production workloads exceeding tens of thousands of records, migrate to SQLite, PostgreSQL, or MongoDB. Projects like `json-server-typeorm` provide JSON Server-compatible APIs with database-backed indexing and query optimization.

## Summary

- **JSON Server loads the entire JSON file into memory** via `lowdb`, creating a hard dependency on available Node.js heap space.
- **All queries execute linear O(N) scans** through `matchesWhere()` in [`src/service.ts`](https://github.com/typicode/json-server/blob/main/src/service.ts), making large collections CPU-intensive to filter.
- **Every mutation triggers a full file rewrite** through `NormalizedAdapter.write()`, causing write latency to scale linearly with file size.
- **Embedding and sorting compound overhead** by introducing O(N × M) complexity and full-array memory operations.
- **Mitigation requires architectural changes**: pagination limits payload but not scan cost, while true scalability requires database migration or sharding across multiple instances.

## Frequently Asked Questions

### What is the maximum file size JSON Server can handle?

JSON Server does not enforce a hard file size limit, but practical constraints emerge around 50-100 MB depending on available system memory. The entire file must fit within the Node.js heap (default 1.5 GB), and large files risk `HEAP OUT OF MEMORY` errors during garbage collection or when loading complex nested objects.

### Why does JSON Server slow down as my database grows?

Performance degradation stems from three architectural characteristics: the entire database resides in memory requiring linear scans for every query via `matchesWhere()` in [`src/matches-where.ts`](https://github.com/typicode/json-server/blob/main/src/matches-where.ts), every write operation rewrites the complete file through `NormalizedAdapter.write()`, and complex operations like `_embed` or sorting process full arrays in memory. These O(N) and O(N × M) operations become measurably slower as N grows.

### Can I use JSON Server in production with large datasets?

JSON Server is designed for prototyping and mocking APIs rather than production workloads with large datasets. While you can mitigate issues using pagination, avoiding embeds, and splitting databases across instances, the fundamental limitations of in-memory storage and linear scanning make it unsuitable for high-traffic or large-volume production scenarios. For production, migrate to indexed databases like SQLite or PostgreSQL.

### How can I improve query speed without switching databases?

To improve query performance within JSON Server's constraints: implement strict pagination using `_page` and `_per_page` to minimize final payload size, eliminate `_embed` parameters to avoid nested iteration, simplify `_where` clauses to use equality checks rather than operators like `_gt` or `_contains`, and shard your data across multiple JSON Server instances to reduce per-process memory pressure and scan scope. Note that these strategies reduce overhead but cannot eliminate the linear scan requirement inherent to the architecture.