JSON
06 / 06

JSON Schema, Validation & Tooling

JSON Schema, Validation & Tooling

JSON Schema Basics

JSON Schema is a vocabulary for annotating and validating JSON. Used in API contracts (OpenAPI), config validation, form validation, and IDE autocomplete.

{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://example.com/user.schema.json",
  "title": "User",
  "type": "object",
  "required": ["id", "name", "email"],
  "additionalProperties": false,
  "properties": {
    "id": {
      "type": "integer",
      "minimum": 1
    },
    "name": {
      "type": "string",
      "minLength": 2,
      "maxLength": 100
    },
    "email": {
      "type": "string",
      "format": "email"
    },
    "role": {
      "type": "string",
      "enum": ["admin", "user", "guest"]
    },
    "age": {
      "type": "integer",
      "minimum": 0,
      "maximum": 150
    },
    "tags": {
      "type": "array",
      "items": { "type": "string" },
      "uniqueItems": true
    },
    "address": {
      "$ref": "#/$defs/Address"
    }
  },
  "$defs": {
    "Address": {
      "type": "object",
      "required": ["street", "city"],
      "properties": {
        "street": { "type": "string" },
        "city": { "type": "string" },
        "zip": { "type": "string", "pattern": "^[0-9]{5}$" }
      }
    }
  }
}

Validation with Zod (TypeScript)

import { z } from 'zod';

const UserSchema = z.object({
  id: z.number().int().positive(),
  name: z.string().min(2).max(100),
  email: z.string().email(),
  role: z.enum(['admin', 'user', 'guest']).default('user'),
  age: z.number().int().min(0).max(150).optional(),
  tags: z.array(z.string()).default([]),
  createdAt: z.string().datetime().transform(s => new Date(s)),
});

type User = z.infer<typeof UserSchema>;

// Parse (throws on invalid)
const user = UserSchema.parse(rawData);

// Safe parse (returns result object, never throws)
const result = UserSchema.safeParse(rawData);
if (result.success) {
  console.log(result.data);
} else {
  console.error(result.error.format());
  // { name: { _errors: ['String must contain at least 2 character(s)'] } }
}

// Validate API request body in Next.js
export async function POST(req: Request) {
  const body = await req.json();
  const parsed = UserSchema.safeParse(body);
  if (!parsed.success) {
    return Response.json({ errors: parsed.error.format() }, { status: 400 });
  }
  return Response.json(await createUser(parsed.data));
}

jq — Command-Line JSON Processor

# Pretty print
cat data.json | jq .
curl -s api.example.com/users | jq .

# Extract field
jq '.name' data.json
jq '.users[0].email' data.json

# Filter array
jq '.users[] | select(.age > 18)' data.json
jq '.users[] | select(.role == "admin") | .name' data.json

# Transform: build new object
jq '.users[] | {id, name, isAdmin: (.role == "admin")}' data.json

# Get array of values
jq '[.users[].name]' data.json
jq '[.users[] | select(.active)] | length' data.json

# Modify values
jq '.users[] | .name = (.name | ascii_upcase)' data.json

# Combine files
jq -s '.[0] * .[1]' base.json override.json   # merge

# Output as TSV
jq -r '.users[] | [.id, .name, .email] | @tsv' data.json

# Process API responses
curl -s "https://api.github.com/repos/vercel/next.js" | \
  jq '{name, stars: .stargazers_count, forks: .forks_count, language}'

JSON Patch (RFC 6902)

JSON Patch describes changes to a JSON document as an array of operations. Used in REST APIs for partial updates (PATCH method).

[
  { "op": "add",     "path": "/tags/-",        "value": "typescript" },
  { "op": "remove",  "path": "/deprecated"                            },
  { "op": "replace", "path": "/name",           "value": "New Name"  },
  { "op": "move",    "from": "/author/name",    "path": "/authorName"},
  { "op": "copy",    "from": "/template",       "path": "/instance"  },
  { "op": "test",    "path": "/version",        "value": "2.0"       }
]

JSON5 & JSONC

  • JSON5: superset of JSON allowing comments, trailing commas, single quotes, unquoted keys, multiline strings. Used in some config files.

  • JSONC (JSON with Comments): VS Code's format for settings.json, tsconfig.json. Comments allowed, trailing commas sometimes.

  • tsconfig.json, .eslintrc.json use JSONC — VS Code handles them with special parser.

  • For programmatic use, parse JSONC with json5 or strip-json-comments npm packages.

JSON in Databases

  • PostgreSQL: json (stored as text, validated) vs jsonb (binary, indexable, operators). Prefer jsonb.

  • PostgreSQL jsonb operators: -> (get key), ->> (get key as text), #> (path), @> (contains), ? (key exists)

  • MySQL: JSON type with JSON_EXTRACT(), JSON_SET(), JSON_CONTAINS(). Path: $.key.nested

  • MongoDB: native BSON (superset of JSON). Query nested: {"address.city": "Kyiv"}

  • SQLite: JSON1 extension with json_extract(), json_object(), json_array()

Keep your own version of these notes — editable, searchable, and organised by your stack.

Start free