How to Handle Errors and Validation in JSON Server
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 and 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 for startup errors and 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:
- 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
hadReadErrorflag, 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:
- 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
withBodyandwithIdAndBodywrappers check if the request body is a plain object usingisItem(req.body). If not, they skip the action, allowing downstream handlers to return 404 or an empty response. - Invalid
_whereparameter (lines 43-51): If the client supplies malformed JSON in the_wherequery 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 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:
// 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:
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:
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 silently ignores malformed _where JSON. To enforce strict validation, modify the parsing logic to return 400 for invalid JSON:
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.tsfor startup errors (missing/empty/corrupt JSON files) andsrc/app.tsfor request handling (404s, invalid bodies, malformed_whereparameters). - Validation gaps: No schema enforcement, required-field checks, or type coercion—developers must implement these.
- Extension pattern: Use custom middleware wrapping
withBodyorwithIdAndBodyto inject validation logic, return 400 for validation failures, and usesafeHandlerwrappers 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, 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 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 (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 with enterprise-grade validation.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →