json-server: Create a Mock REST API from a JSON File in Minutes
json-server is an npm package that creates a complete fake REST API from a single db.json file in under 60 seconds. It supports GET, POST, PUT, PATCH, and DELETE requests, query-string filtering, pagination, sorting, full-text search, and relationships — with zero configuration required. The package has over 3 million weekly npm downloads and is the most popular tool for mocking REST APIs during frontend development and testing. Install with npm install -g json-server, create a db.json file, and run json-server --watch db.json to start a server on port 3000. Need to format your JSON before using it? Jsonic's JSON Formatter cleans it up instantly.
Need to format or validate your db.json before starting the server? Jsonic's JSON Formatter handles it in one click.
Open JSON FormatterInstall and start json-server
Install globally to use from any directory, or locally per project for team consistency. The global install is faster to get started; the project-level install ensures every developer on the team uses the same version and the command is captured in package.json. Create a db.json with at least one top-level array — json-server automatically exposes a full set of REST endpoints for each array key. You can also generate fake JSON data to populate db.json with realistic values before starting the server.
# Global install
npm install -g json-server
# Project-level install (recommended)
npm install --save-dev json-serverCreate a db.json file:
{
"posts": [
{ "id": 1, "title": "Hello World", "author": "Alice" },
{ "id": 2, "title": "JSON Guide", "author": "Bob" }
],
"comments": [
{ "id": 1, "postId": 1, "body": "Great post!" }
],
"users": [
{ "id": 1, "name": "Alice", "email": "alice@example.com" }
]
}Start the server with the --watch flag so it hot-reloads when db.json changes:
json-server --watch db.jsonThis immediately exposes: GET /posts, GET /posts/1, POST /posts, PUT /posts/1, PATCH /posts/1, DELETE /posts/1 — and the same set for /comments and /users. Add a convenience script to package.json to avoid typing the full command every time:
{
"scripts": {
"mock": "json-server --watch db.json --port 3001"
}
}Then run npm run mock. Using port 3001 avoids collisions with a frontend dev server that typically runs on 3000.
CRUD operations with curl
json-server supports all five HTTP methods on every resource. Write operations (POST, PUT, PATCH, DELETE) are immediately persisted to db.json, so the data survives server restarts. POST auto-assigns an incremented id — you do not need to include it in the request body. Before sending data, you can validate your JSON to ensure the payload is well-formed.
# GET all posts
curl http://localhost:3000/posts
# GET one post by id
curl http://localhost:3000/posts/1
# POST: create a new post (id auto-assigned)
curl -X POST \
-H "Content-Type: application/json" \
-d '{"title":"New","author":"Carol"}' \
http://localhost:3000/posts
# PUT: replace a post entirely
curl -X PUT \
-H "Content-Type: application/json" \
-d '{"id":1,"title":"Updated","author":"Alice"}' \
http://localhost:3000/posts/1
# PATCH: update specific fields only
curl -X PATCH \
-H "Content-Type: application/json" \
-d '{"title":"Patched"}' \
http://localhost:3000/posts/1
# DELETE a post
curl -X DELETE http://localhost:3000/posts/1PUT requires the full object including id — it replaces the record entirely. PATCH accepts a partial object and merges it with the existing record, leaving other fields unchanged. All changes are written to db.json immediately after each request. To parse the JSON responses in a frontend application, use response.json() in the Fetch API or axios.get().
Filtering, sorting, and pagination
json-server provides a rich set of query parameters for filtering, sorting, and paginating results without any additional configuration. All parameters are passed as query strings appended to the resource URL. Pagination returns a slice of results and sets the X-Total-Count response header with the total number of records, which is essential for building paginated UI components. See JSON examples for more patterns to use with these endpoints.
# Filter by field value
GET /posts?author=Alice
# Multiple filters (AND)
GET /posts?author=Alice&title=Hello
# Sort ascending or descending
GET /posts?_sort=title&_order=asc
GET /posts?_sort=title&_order=desc
# Pagination (sets X-Total-Count header)
GET /posts?_page=1&_limit=10
# Slice (zero-based, end is exclusive)
GET /posts?_start=0&_end=5
# Full-text search across all string fields
GET /posts?q=hello
# Range filters
GET /posts?price_gte=10&price_lte=50
# Not-equal filter
GET /posts?author_ne=Alice
# Regex / partial match
GET /posts?title_like=JSONThe _page and _limit parameters are the most commonly used for paginating large datasets. The server sets the X-Total-Count header on every paginated response — read it in your frontend with response.headers.get('X-Total-Count') to calculate total pages. The _like operator accepts a JavaScript regex pattern, making it useful for prefix searches (title_like=^Hello) and contains searches (title_like=World).
Relationships: embedding and expanding
json-server follows a foreign key convention to link related collections. If a child collection (e.g., comments) has records with a field named {singular_parent_name}Id (e.g., postId), json-server recognises those as related to the parent collection (posts). This convention enables two query parameters — _embed and _expand — that join related records at query time without any additional configuration.
# Expand: include the parent object in a child response
# (looks up userId field on each comment)
GET /comments?_expand=post
# Embed: include children in each parent response
# (finds all comments with matching postId)
GET /posts?_embed=comments
# Shorthand: get all comments for post 1
GET /posts/1/comments
# equivalent to: GET /comments?postId=1
# Combine both in one request
GET /posts/1?_embed=commentsThe convention is strict: the child must have a field named exactly {singularParentName}Id. So userId links to users, postId links to posts. json-server does not enforce referential integrity — deleting a parent does not cascade to children — but it handles the read-side joins correctly. For deeply nested relationships, you can chain multiple _embed calls (v0) or use the array syntax _embed[]=comments in v1.
Custom routes with routes.json
The routes.json file lets you define URL rewrites so json-server can respond at paths that match your real API's URL structure. This is useful when your frontend is hardcoded to a specific URL scheme (e.g., /api/v1/posts) and you want the mock server to respond at those paths without changing your frontend code. Rewrites use * as a wildcard and :param syntax for named segments.
// routes.json
{
"/api/*": "/$1",
"/blog/:id/comments": "/comments?postId=:id",
"/v1/products": "/products"
}Start json-server with the routes file:
json-server --watch db.json --routes routes.jsonNow GET /api/posts maps to GET /posts, GET /blog/1/comments maps to GET /comments?postId=1, and GET /v1/products maps to GET /products. The /api/* wildcard rule is the most commonly used — it lets you prefix all routes with /api/ so they match a typical backend URL structure. Multiple rules are evaluated in order; the first match wins.
Custom middleware with server.js
For more control than the CLI provides, use json-server programmatically by requiring it in a server.js file. The programmatic API wraps Express, so any Express-compatible middleware works. Common uses include adding timestamps to created records, simulating network latency, logging requests, and modifying responses on the fly. This approach also lets you serve static files alongside the mock API.
// server.js
const jsonServer = require('json-server')
const server = jsonServer.create()
const router = jsonServer.router('db.json')
const middlewares = jsonServer.defaults()
// Add custom middleware — runs before every request
server.use((req, res, next) => {
if (req.method === 'POST') {
req.body.createdAt = new Date().toISOString()
}
next()
})
// Simulate 300ms network delay
server.use((req, res, next) => {
setTimeout(next, 300)
})
server.use(middlewares)
server.use(router)
server.listen(3000, () => {
console.log('JSON Server running on port 3000')
})Run the custom server with node server.js instead of the json-server CLI. The jsonServer.defaults() middleware includes static file serving, CORS headers, gzip compression, and the logger. You can pass options to it: jsonServer.defaults({ logger: false }) to disable request logging. Add the script to package.json: "mock": "node server.js".
Add authentication simulation
Simulating authentication in a mock server lets you test how your frontend handles 401 errors, token expiry, and protected routes without building a real auth backend. The middleware approach in server.js makes this straightforward — intercept requests before they reach the router and check for an authorization header. You can return a realistic 401 JSON response that mirrors your real API's error format.
// server.js — add before server.use(middlewares)
const auth = (req, res, next) => {
if (req.headers.authorization === 'Bearer my-secret-token') {
next()
} else {
res.status(401).json({ error: 'Unauthorized' })
}
}
// Protect write operations; allow all GET requests
server.use((req, res, next) => {
if (req.path === '/public' || req.method === 'GET') {
return next()
}
auth(req, res, next)
})
server.use(middlewares)
server.use(router)
server.listen(3000)This pattern allows all GET requests and the /public path without authentication, but requires the token for POST, PUT, PATCH, and DELETE. To test from the command line: curl -H "Authorization: Bearer my-secret-token" -X POST .... For more sophisticated scenarios, generate a JWT on a /login route and verify it on subsequent requests using the jsonwebtoken package.
Generate large mock datasets with @faker-js/faker
Hardcoding a few records in db.json works for simple tests, but realistic performance testing and UI development require hundreds or thousands of records with varied, realistic data. Combine json-server with @faker-js/faker to generate a large db.json programmatically. This also makes it easy to regenerate fresh data between test runs. See the JSON mock data generator guide for more techniques.
// generate-db.js
const { faker } = require('@faker-js/faker')
const db = {
users: Array.from({ length: 100 }, (_, i) => ({
id: i + 1,
name: faker.person.fullName(),
email: faker.internet.email(),
city: faker.location.city(),
})),
products: Array.from({ length: 200 }, (_, i) => ({
id: i + 1,
name: faker.commerce.productName(),
price: parseFloat(faker.commerce.price()),
category: faker.commerce.department(),
})),
}
require('fs').writeFileSync('db.json', JSON.stringify(db, null, 2))
console.log('Generated db.json with', db.users.length, 'users and', db.products.length, 'products')Install faker: npm install --save-dev @faker-js/faker. Then generate and start in one command:
node generate-db.js && json-server --watch db.jsonAdd a generate script to package.json and call it as part of your test setup. The generated db.json can also be committed to the repository as a stable fixture for CI environments where you want reproducible data — seed faker with a fixed value: faker.seed(42).
json-server v1 (latest) vs v0 differences
json-server v1 (released 2024) is a full rewrite with breaking changes from the v0 series that most tutorials and documentation target. If you install the latest version and find that examples from the internet do not work, check your version: json-server --version. The key differences between v1 and v0:
- Module format: v1 uses ESM (
import/export), v0 uses CommonJS (require/module.exports). - Node.js requirement: v1 requires Node.js 18+; v0 works on Node.js 12+.
- Query syntax: v1 changes
_embed/_expandto array syntax:_embed[]=commentsinstead of_embed=comments. - Dropped operators: v1 removes some v0 query operators (e.g.,
_likebehavior may differ). - Programmatic API: v1 changes the
require('json-server')programmatic interface —server.jsfiles from v0 tutorials need updates.
v0.17.x remains the most widely used and documented version. To install a specific version: npm install --save-dev json-server@0.17.4. Unless you have a specific reason to use v1 (ESM project, Node.js 18+ features), pinning to v0.17.x gives you the most community support and up-to-date tutorial coverage.
Frequently asked questions
What is json-server?
json-server is an open-source npm package by Typicode that creates a full fake REST API from a JSON file or JavaScript object. It is designed for frontend developers who need a working backend for development and testing without building an actual server. Given a db.json file with top-level arrays, json-server exposes REST endpoints for each collection: list all (GET), get one by id (GET /:id), create (POST), replace (PUT /:id), update (PATCH /:id), and delete (DELETE /:id). It supports filtering, sorting, pagination, full-text search, and relationship expansion through query parameters. All write operations are immediately persisted to the JSON file, so your mock data survives server restarts. json-server has over 3 million weekly npm downloads and 72,000+ GitHub stars, making it the most widely used tool for mocking REST APIs in the JavaScript ecosystem.
How do I install and start json-server?
For a global install (accessible from any directory): npm install -g json-server. Then create a db.json file with some data and run json-server --watch db.json. The server starts on port 3000 by default. Use --port 3001 to change the port: json-server --watch db.json --port 3001. For a project-level install (recommended for team projects): npm install --save-dev json-server, then add a script to package.json: "mock": "json-server --watch db.json --port 3001" and run npm run mock. The --watch flag enables hot-reload: editing db.json restarts the server automatically. Check the running server at http://localhost:3000 — the index page lists all available endpoints.
How do I filter, sort, and paginate results in json-server?
json-server supports query parameters for filtering, sorting, pagination, and search. Filtering: GET /posts?author=Alice returns posts where author equals Alice. Multiple filters are ANDed: ?author=Alice&published=true. Sorting: ?_sort=createdAt&_order=desc sorts by the createdAt field descending. Pagination: ?_page=2&_limit=10 returns the second page of 10 items; the X-Total-Count response header contains the total record count. Full-text search: ?q=hello searches all string fields in every record. Range filters: ?price_gte=10&price_lte=50 returns records where price is between 10 and 50. The _ne operator excludes values (?status_ne=deleted). The _like operator filters by regex pattern (?name_like=^A).
How do I add relationships in json-server?
json-server automatically handles relationships by convention. If a comments collection has records with a postId field, those comments are related to the posts collection. You can then: (1) get all comments for a post with GET /posts/1/comments; (2) embed comments in post responses with GET /posts?_embed=comments; (3) expand (include) the parent post in a comment response with GET /comments?_expand=post. The convention is: the child collection must have a field named {singular_parent_resource}Id — so userId links to users, postId links to posts. Foreign key fields are not enforced at the database level, but the _embed and _expand query parameters use these conventions to join related records at query time.
Can I add custom middleware or logic to json-server?
Yes. Using json-server programmatically (via require('json-server')) lets you add Express middleware, custom routes, authentication, response delays, and data transforms. Create a server.js file: require json-server, create a server, add middleware with server.use(), attach the router, and call server.listen(). Common uses: (1) add simulated network delay — server.use((req, res, next) => { setTimeout(next, 500) }); (2) add authentication headers check; (3) intercept POST requests to add createdAt/updatedAt timestamps; (4) serve different responses based on a custom header or query parameter. The programmatic API is fully Express-compatible, so any Express middleware works. Run the custom server with node server.js instead of the json-server CLI command.
What are alternatives to json-server?
Common alternatives include: MSW (Mock Service Worker) — intercepts requests at the browser/Node.js level, no separate process needed, integrates with testing frameworks (Vitest, Jest); Mirage JS — in-memory REST API with schema definitions, no separate process; Mockoon — GUI-based mock server with visual editing; Beeceptor — cloud-based mock API service; Prism — generates mocks automatically from OpenAPI/Swagger specs. json-server is best when: you want a persistent JSON file as a database, you need a real HTTP server separate from your frontend process (useful for native mobile apps or non-browser clients), or you want full REST semantics with minimal setup. MSW is generally preferred for browser-based unit/integration tests because it does not require a separate server process.
Ready to mock your REST API with json-server?
Use Jsonic to format and validate your db.json before starting the server. You can also generate fake JSON data to populate db.json with realistic records instantly.