JSON Toolbox LogoJSONToolbox

JSON Validation Explained: Syntax Checks vs JSON Schema Validation

JSON Toolbox Team
Pro Tip

Validate your JSON data against a JSON Schema:

Open JSON Schema Validator →

JSON Validation Explained: Syntax Checks vs JSON Schema Validation

A JSON string can be parsed without errors and still break your API.

JSON.parse() only checks whether text follows JSON grammar. It does not check whether required fields exist, whether values match expected types, or whether extra fields should be rejected. For that, you need schema validation and, beyond that, business-rule validation.

This guide explains the three layers of JSON validation and shows how to implement each one in JavaScript.

The Short Answer: Valid JSON Is Not Always Valid Data

Consider this payload sent to a POST /users endpoint:

{
  "email": "not-an-email",
  "age": -3,
  "role": "superadmin",
  "marketingOptIn": "yes",
  "debug": true
}

This is valid JSON — JSON.parse() will succeed. But the data violates nearly every rule your API should enforce:

  • email is not a valid email address.
  • age is negative.
  • role is not one of the allowed values.
  • marketingOptIn should be a boolean, not a string.
  • debug is an unknown field that should not be accepted.

This is why validation must go beyond syntax.

Layer 1: Syntax Validation — Can the Text Be Parsed?

Syntax validation checks whether a string conforms to the JSON grammar defined in RFC 8259. The simplest way to perform it in JavaScript is JSON.parse().

What JSON.parse() checks

JSON.parse() verifies that the text is structurally valid JSON:

  • Strings use double quotes (", not ').
  • Keys are double-quoted.
  • No trailing commas.
  • No comments.
  • Brackets and braces are balanced.
  • Numbers, booleans, and null are in correct form.

If any of these rules are violated, JavaScript throws a SyntaxError. Common problems include trailing commas from JavaScript object literals, single-quoted strings, and unquoted keys.

Common syntax errors

This JSON looks reasonable but fails to parse:

{
  "email": "ada@example.com",
  "age": 36,
}

The trailing comma after 36 makes it invalid. Fix:

{
  "email": "ada@example.com",
  "age": 36
}

Other frequent mistakes:

  • Single quotes instead of double quotes: { 'name': 'Ada' } → invalid.
  • Comments: { "name": "Ada" // developer note } → invalid.
  • Unquoted keys: { name: "Ada" } → invalid in JSON (valid in JavaScript object literals).
  • Unclosed brackets: { "items": [1, 2, 3 → invalid.

A safe syntax-check helper

Instead of wrapping JSON.parse() in a bare try/catch, return a structured result so callers can display meaningful error messages:

export type JsonSyntaxResult =
  | { valid: true; value: unknown }
  | { valid: false; error: string }

export function parseJsonSafely(text: string): JsonSyntaxResult {
  try {
    return { valid: true, value: JSON.parse(text) }
  } catch (error) {
    return {
      valid: false,
      error: error instanceof Error ? error.message : 'Invalid JSON',
    }
  }
}

This only verifies JSON syntax. It does not verify required fields, types, allowed values, or application rules. If JSON.parse() throws before you can inspect the data, start with our guide to debugging common JSON parse errors in API responses.

When to use an online JSON Validator

For quick checks during development or when reviewing API responses from logs, paste the raw text into a JSON syntax validator to see whether it parses and where errors occur. This is especially useful when the JSON is minified or comes from an unfamiliar source.

Layer 2: Schema Validation — Does the Data Have the Expected Shape?

Syntax validation tells you the text is JSON. Schema validation tells you the data matches the structure your application expects.

What JSON Schema validates

JSON Schema is a declarative vocabulary for describing the structure, constraints, and data types of JSON documents. A schema can specify:

  • The expected type: object, array, string, number, boolean, or null.
  • Which fields are required.
  • Allowed values for each field.
  • Numeric ranges, string patterns, and array lengths.
  • Whether extra fields are permitted.

The schema is itself a JSON document, which makes it portable across languages and tools.

The user creation schema

Here is a JSON Schema for the POST /users payload. It uses Draft 2020-12:

{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "additionalProperties": false,
  "required": ["email", "age", "role", "marketingOptIn"],
  "properties": {
    "email": {
      "type": "string",
      "format": "email"
    },
    "age": {
      "type": "integer",
      "minimum": 0,
      "maximum": 150
    },
    "role": {
      "type": "string",
      "enum": ["user", "editor", "admin"]
    },
    "marketingOptIn": {
      "type": "boolean"
    }
  }
}

How to read this schema

KeywordWhat it enforces
type: "object"The root value must be a JSON object.
requiredThese fields must be present.
propertiesDefines the expected shape for each field.
format: "email"Declares the expected format. Enforcement depends on the validator (see below).
minimum / maximumNumeric boundaries for age.
enumRestricts role to the listed values.
additionalProperties: falseRejects any field not declared in properties or patternProperties.

Two details that often cause confusion:

  1. properties does not make fields required. Listing a field in properties only defines its schema. You must also list it in required to make it mandatory. A field can appear in properties but be absent from required, making it optional.
  2. additionalProperties defaults to allowing extra fields. If you do not set it to false, an object with unexpected fields like "debug": true will pass validation. Only an explicit additionalProperties: false rejects undeclared fields.

The format keyword caveat

The format keyword communicates an intended format, but whether it is enforced depends on the JSON Schema validator and its configuration. For example, Ajv v7+ provides common format validators through the optional ajv-formats package. Without it, "format": "email" is treated as an annotation, not a validation rule. Always verify your validator's configuration before relying on format checks in production.

A valid JSON document that fails schema validation

The payload from the introduction passes JSON.parse() but fails the schema:

{
  "email": "not-an-email",
  "age": -3,
  "role": "superadmin",
  "marketingOptIn": "yes",
  "debug": true
}

A schema validator would report these errors:

PathProblem
/emailValue does not match format email.
/ageValue -3 is less than minimum 0.
/roleValue superadmin is not one of the allowed enum values.
/marketingOptInExpected boolean, got string.
/debugProperty debug is not allowed when additionalProperties is false.

You can test this interactively with the JSON Schema Validator — paste the payload and the schema side by side to see the errors.

Layer 3: Business Validation — Can Your Application Accept This Data?

Schema validation is powerful, but it cannot express rules that depend on your system's state. Business validation handles those cases with application logic.

Examples JSON Schema cannot fully decide

  • The email address ada@example.com is syntactically valid and matches the email format, but it may already exist in your database.
  • An age of 200 passes the minimum: 0 check but may be rejected by a business rule capping realistic ages.
  • A user's role may be valid per the enum, but the authenticated caller may not have permission to assign admin.
  • A startDate and endDate may both be valid ISO 8601 strings, but the start may fall after the end.
  • A product ID may exist in the schema, but the product may be out of stock.

These checks require querying a database, verifying permissions, or running application-specific logic. No JSON Schema can replace them.

Client-side versus server-side validation

Run schema validation on both the client and the server when it improves user feedback. But always validate again on the backend. Client-side checks:

  • Can be bypassed by modifying network requests.
  • Cannot safely enforce permissions or database constraints.
  • Should be treated as a UX convenience, not a security measure.

How to Validate JSON in JavaScript

Here is a complete validation pipeline for the POST /users endpoint.

Step 1: Syntax validation

import Ajv from 'ajv'
import addFormats from 'ajv-formats'

const ajv = new Ajv({ allErrors: true, strict: true })
addFormats(ajv)

const createUserSchema = {
  type: 'object',
  additionalProperties: false,
  required: ['email', 'age', 'role', 'marketingOptIn'],
  properties: {
    email: { type: 'string', format: 'email' },
    age: { type: 'integer', minimum: 0, maximum: 150 },
    role: { type: 'string', enum: ['user', 'editor', 'admin'] },
    marketingOptIn: { type: 'boolean' },
  },
} as const

const validateCreateUser = ajv.compile(createUserSchema)

Ajv compiles the schema into a reusable validation function. It supports multiple JSON Schema drafts and reports all errors when allErrors is set to true.

Step 2: Schema validation

export function validateCreateUserPayload(value: unknown) {
  const valid = validateCreateUser(value)
  return {
    valid: Boolean(valid),
    errors: validateCreateUser.errors ?? [],
  }
}

Step 3: The full pipeline

export function validateIncomingUserJson(text: string) {
  // Layer 1: Syntax
  const parsed = parseJsonSafely(text)
  if (!parsed.valid) {
    return { ok: false as const, stage: 'syntax' as const, errors: [parsed.error] }
  }

  // Layer 2: Schema
  const schemaResult = validateCreateUserPayload(parsed.value)
  if (!schemaResult.valid) {
    return { ok: false as const, stage: 'schema' as const, errors: schemaResult.errors }
  }

  // Layer 3: Business (example — you would implement this with your DB/logic)
  // if (await emailAlreadyExists(parsed.value.email)) {
  //   return { ok: false, stage: 'business', errors: ['Email is already registered'] }
  // }

  return { ok: true as const, value: parsed.value }
}

This function runs the cheapest check first. If syntax fails, there is no point running schema validation. If schema fails, there is no point querying the database.

API Error Response Design

A well-designed API returns different error shapes depending on which validation layer failed.

Syntax error → 400 Bad Request

{
  "error": {
    "code": "INVALID_JSON",
    "message": "Request body is not valid JSON."
  }
}

Schema error → 422 Unprocessable Entity

{
  "error": {
    "code": "VALIDATION_FAILED",
    "message": "Request data does not match the expected schema.",
    "fields": [
      { "path": "/email", "message": "must match format \"email\"" },
      { "path": "/age", "message": "must be >= 0" },
      { "path": "/marketingOptIn", "message": "must be boolean" }
    ]
  }
}

Business-rule error → 409 Conflict or 422

{
  "error": {
    "code": "EMAIL_ALREADY_EXISTS",
    "message": "An account already uses this email address."
  }
}

Many APIs use 400 for malformed JSON and 422 for structurally valid data that fails validation, but your API should follow one documented and consistent convention.

Common JSON Validation Mistakes

Mistake 1: Treating JSON.parse() success as API validation

JSON.parse() succeeding only means the text is valid JSON. It says nothing about whether the data matches your API contract. Always run schema validation after parsing.

Mistake 2: Relying on Content-Type alone

A request with Content-Type: application/json header may still contain a body that is not valid JSON. Always parse and validate the body, not just the header.

Mistake 3: Allowing unknown fields accidentally

If your schema does not include additionalProperties: false, unexpected fields pass through silently. This can leak internal debug data into your system or cause subtle bugs when clients send fields you did not expect.

Mistake 4: Coercing values without documenting it

Some frameworks silently convert "42" to 42 or "true" to true. If your API does this, document it clearly. Silent coercion can hide client bugs and make debugging harder. When in strict mode, Ajv rejects type mismatches rather than coercing them.

Mistake 5: Validating only in the browser

Client-side validation improves UX but can be bypassed. Every request that modifies data must be validated again on the server.

Mistake 6: Logging raw invalid payloads with secrets

When logging validation failures, strip or redact sensitive fields like tokens, passwords, and API keys before writing to logs.

A Practical API Validation Workflow

For each incoming JSON request:

  1. Enforce a body-size limit. Reject payloads that exceed your expected maximum before parsing.
  2. Parse the JSON. Use JSON.parse() or an equivalent. If it fails, return a 400 error with the parse error message.
  3. Validate against a schema. Check required fields, types, ranges, and additional properties. If it fails, return a 422 error with the list of schema violations.
  4. Apply business rules. Check database constraints, permissions, and application logic. If it fails, return a 409 or 422 error with a specific error code.
  5. Return structured errors. Include a machine-readable error code, a human-readable message, and field-level details for schema errors.
  6. Log safely. Redact sensitive fields before writing to logs.

FAQ

Does JSON.parse() validate JSON Schema?

No. JSON.parse() only checks whether a string follows JSON syntax and converts it into a JavaScript value. It does not check whether required fields exist, whether values have expected types, or whether extra fields are allowed. Use a schema validator like Ajv for that.

Is valid JSON always safe to use as API data?

No. A payload may be syntactically valid JSON but still fail your API contract. For example, an age field may be negative, a required field may be missing, or a role may not be one of the allowed values. Syntax validation is only the first layer.

Should I validate JSON in the frontend or backend?

Validate on both when it improves user feedback, but always validate again on the backend. Client-side checks can be bypassed and cannot safely enforce permissions, database constraints, or other server-side business rules.

Does "format": "email" always validate email addresses?

Not necessarily. JSON Schema validators differ in how they implement and enable format checks. With Ajv, common formats are provided through the ajv-formats package, so confirm your validator configuration before relying on format validation in production.

What's Next?


All tools on JSON Toolbox run entirely in your browser. Your data never leaves your device.

#JSON #JSON Validation #JSON Schema #JavaScript #API Validation #Data Validation

Have a question or feedback? Contact us