# How to Handle Errors and Validation in JSON Server

> Learn to handle errors and validation in JSON Server with custom middleware. Implement robust input validation and error management for your mock API.

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

---

**JSON Server provides minimal built-in error handling and no automatic request-body validation, requiring developers to implement custom middleware for robust input validation and error management.**

JSON Server by typicode is a lightweight tool that creates a full fake REST API from a JSON file. While it handles basic HTTP errors like 404s for missing resources, it intentionally leaves request validation and complex error handling to the developer. Understanding the core error handling in [`src/bin.ts`](https://github.com/typicode/json-server/blob/main/src/bin.ts) and [`src/app.ts`](https://github.com/typicode/json-server/blob/main/src/app.ts) allows you to extend the server with proper validation logic without breaking its low-overhead nature.

## Understanding JSON Server's Built-In Error Handling

JSON Server handles errors at two levels: the CLI startup phase and the HTTP request processing phase. The core logic resides in [`src/bin.ts`](https://github.com/typicode/json-server/blob/main/src/bin.ts) for startup errors and [`src/app.ts`](https://github.com/typicode/json-server/blob/main/src/app.ts) for request-level handling.

### CLI-Level Error Handling (src/bin.ts)

When starting JSON Server from the command line, the CLI performs several safety checks in [`src/bin.ts`](https://github.com/typicode/json-server/blob/main/src/bin.ts):

- **Missing database file** (lines 17-20): If the specified JSON file does not exist, the CLI prints a red error message and aborts the process immediately.
- **Empty JSON file** (lines 22-26): If the file exists but is empty, JSON Server rewrites it to `{}` (empty object) so the server can start without crashing.
- **Syntax errors** (lines 16-22): If the JSON file contains malformed syntax, the server logs the parsing error in red, sets a `hadReadError` flag, and skips the reload until the file is fixed.

### Request-Level Error Handling (src/app.ts)

Once running, JSON Server handles HTTP request errors through middleware in [`src/app.ts`](https://github.com/typicode/json-server/blob/main/src/app.ts):

- **Resource not found** (lines 55-63): When a GET, PUT, POST, or DELETE targets a non-existent collection or ID, the service returns `undefined`. The route handler converts this into a **404** response.
- **Invalid request body** (lines 70-84): The `withBody` and `withIdAndBody` wrappers check if the request body is a plain object using `isItem(req.body)`. If not, they skip the action, allowing downstream handlers to return **404** or an empty response.
- **Invalid `_where` parameter** (lines 43-51): If the client supplies malformed JSON in the `_where` query parameter, the parser silently falls back to normal URL-parameter filtering, ignoring the malformed JSON without throwing an error.

## What JSON Server Does Not Validate

JSON Server intentionally avoids several validation responsibilities:

- **No schema enforcement** – It does not read JSON Schema or TypeScript types to validate incoming payloads.
- **No required-field checks** – It accepts any object shape as long as the request body is an object (`isItem`).
- **No automatic type coercion** – Values are stored exactly as received; numeric strings remain strings.

This minimalist approach keeps JSON Server fast and unopinionated, but it means you must implement custom validation for production-like error handling.

## Implementing Custom Validation in JSON Server

To add robust error handling and validation, extend the middleware layer in [`src/app.ts`](https://github.com/typicode/json-server/blob/main/src/app.ts) using the patterns JSON Server already provides.

### Creating Validation Middleware

Replace the generic `withBody` wrapper with a custom validator that checks domain-specific rules before delegating to the service:

```typescript
// Custom validator example extending src/app.ts patterns
function validateUser(body: Record<string, unknown>) {
  const errors: string[] = []
  if (typeof body.name !== 'string' || body.name.trim() === '') {
    errors.push('`name` is required and must be a non-empty string')
  }
  if (typeof body.age !== 'number' || body.age < 0) {
    errors.push('`age` must be a positive number')
  }
  return errors
}

function withValidatedBody(
  validator: (body: Record<string, unknown>) => string[],
  action: (name: string, body: Record<string, unknown>) => Promise<unknown>,
) {
  return async (req: any, res: any, next: any) => {
    const { name = '' } = req.params
    if (isItem(req.body)) {
      const errors = validator(req.body)
      if (errors.length) {
        res.status(400).json({ errors })
        return
      }
      res.locals['data'] = await action(name, req.body)
    }
    next?.()
  }
}

```

Apply this to your routes:

```typescript
app.post('/:name', withValidatedBody(validateUser, service.create.bind(service)))

```

### Wrapping Routes with Error Handlers

To catch unexpected exceptions and prevent server crashes, wrap service calls in a `safeHandler` that guarantees **500** responses for unhandled errors:

```typescript
function safeHandler(handler: () => any) {
  return async (req: any, res: any, next: any) => {
    try {
      await handler()
      next?.()
    } catch (err) {
      console.error(err)
      res.status(500).json({ error: 'Internal Server Error' })
    }
  }
}

```

Use this wrapper for any async service operations like `service.update` or `service.destroyById` to ensure consistent error responses.

### Validating Query Parameters

The `parseListParams` function in [`src/app.ts`](https://github.com/typicode/json-server/blob/main/src/app.ts) silently ignores malformed `_where` JSON. To enforce strict validation, modify the parsing logic to return **400** for invalid JSON:

```typescript
if (rawWhere) {
  try {
    where = JSON.parse(rawWhere)
  } catch {
    res.status(400).json({ error: '`_where` query parameter is not valid JSON' })
    return
  }
}

```

Place this check before the return statement in `parseListParams` (or expose it as separate middleware) to provide immediate feedback instead of silently falling back to standard filtering.

## Summary

- **JSON Server** handles basic errors like missing files (CLI) and 404s (HTTP), but performs no automatic request-body validation.
- **Core error locations**: [`src/bin.ts`](https://github.com/typicode/json-server/blob/main/src/bin.ts) for startup errors (missing/empty/corrupt JSON files) and [`src/app.ts`](https://github.com/typicode/json-server/blob/main/src/app.ts) for request handling (404s, invalid bodies, malformed `_where` parameters).
- **Validation gaps**: No schema enforcement, required-field checks, or type coercion—developers must implement these.
- **Extension pattern**: Use custom middleware wrapping `withBody` or `withIdAndBody` to inject validation logic, return **400** for validation failures, and use `safeHandler` wrappers to catch unexpected errors and return **500** responses.

## Frequently Asked Questions

### Does JSON Server validate request bodies automatically?

No, JSON Server does not validate request bodies automatically. According to the source code in [`src/app.ts`](https://github.com/typicode/json-server/blob/main/src/app.ts), the `withBody` and `withIdAndBody` wrappers only check if the body is a plain object using `isItem(req.body)`. They do not enforce schema rules, required fields, or data types. You must implement custom middleware to validate request bodies before they reach the service layer.

### How do I return custom error messages in JSON Server?

To return custom error messages, create a validation middleware function that checks the request body and returns a **400** status with a JSON error payload. For example, modify the `withBody` pattern to include a validator parameter that returns an array of error strings. If errors exist, send `res.status(400).json({ errors })` before calling the service action. This approach follows the existing middleware pattern in [`src/app.ts`](https://github.com/typicode/json-server/blob/main/src/app.ts) while adding your custom error responses.

### What happens if the database file is corrupted?

If the database file contains malformed JSON or JSON5 syntax, JSON Server handles this gracefully in [`src/bin.ts`](https://github.com/typicode/json-server/blob/main/src/bin.ts) (lines 16-22). The CLI logs the parsing error in red, sets a `hadReadError` flag, and skips the file reload. The server continues running with the last known good state of the data and will attempt to reload again when the file is fixed and saved. If the file is missing entirely, the server aborts immediately with a "File not found" error.

### Can I use JSON Schema with JSON Server?

Yes, you can use JSON Schema with JSON Server by integrating a validation library like Ajv (Another JSON Schema Validator) into custom middleware. Since JSON Server itself does not read JSON Schema files, you must create a middleware function that validates `req.body` against your schema before passing it to the service layer. If validation fails, return **400** with the schema errors; if it passes, call `next()` or the service action. This extends the default `withBody` pattern in [`src/app.ts`](https://github.com/typicode/json-server/blob/main/src/app.ts) with enterprise-grade validation.