JSON Schema: Validare API come un Pro

THEJORD Team1 min di lettura
jsonschemaapivalidazionetools

JSON Schema: validare API come un professionista. Guida completa per definire strutture dati, validazione automatica e documentazione delle tue API REST.

JSON Schema: Validare API come un Pro

Introduzione a JSON Schema

JSON Schema è uno standard potente per definire la struttura, i vincoli e la validazione dei dati JSON. Nel mondo delle API moderne, dove JSON è il formato di scambio dati predominante, avere uno schema formale che descrive i dati attesi è fondamentale per garantire qualità, documentazione e interoperabilità tra sistemi.

Questa guida completa ti insegnerà a usare JSON Schema come un professionista, dalla sintassi base alle tecniche avanzate utilizzate nelle API enterprise.

Perché Usare JSON Schema

Validazione Automatica

JSON Schema permette di validare automaticamente i dati in ingresso e in uscita dalle API. Questo significa che errori nei dati vengono catturati immediatamente, prima che causino problemi nell'applicazione.

  • Input validation: Verifica che i dati inviati dai client siano corretti
  • Output validation: Garantisce che le risposte API rispettino il contratto
  • Testing: Genera automaticamente test cases basati sullo schema
  • Documentazione: Lo schema stesso è documentazione vivente

Documentazione Automatica

Con JSON Schema puoi generare automaticamente documentazione API usando tool come Swagger/OpenAPI, Redoc o Stoplight. Lo schema diventa la single source of truth per tutta la documentazione.

Sintassi Base

Struttura di Uno Schema

{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://example.com/user.schema.json",
  "title": "User",
  "description": "Schema per validare oggetti utente",
  "type": "object",
  "properties": {
    "id": {
      "type": "integer",
      "description": "ID univoco dell'utente"
    },
    "name": {
      "type": "string",
      "minLength": 1,
      "maxLength": 100
    },
    "email": {
      "type": "string",
      "format": "email"
    }
  },
  "required": ["id", "name", "email"]
}

Tipi di Dati

TipoDescrizioneEsempio
stringStringa di testo"hello"
numberNumero (inclusi decimali)3.14
integerNumero intero42
booleanVero o falsotrue
arrayLista di elementi[1, 2, 3]
objectOggetto con proprietà{"key": "value"}
nullValore nullonull

Vincoli per Tipo

Vincoli per Stringhe

{
  "type": "string",
  "minLength": 1,           // Lunghezza minima
  "maxLength": 255,         // Lunghezza massima
  "pattern": "^[A-Z]{2}$",  // Regex pattern
  "format": "email"         // Formato predefinito
}

// Formati disponibili:
// email, uri, uri-reference, uuid
// date, date-time, time
// hostname, ipv4, ipv6
// regex, json-pointer

Vincoli per Numeri

{
  "type": "number",
  "minimum": 0,              // Valore minimo (incluso)
  "maximum": 100,            // Valore massimo (incluso)
  "exclusiveMinimum": 0,     // Valore minimo (escluso)
  "exclusiveMaximum": 100,   // Valore massimo (escluso)
  "multipleOf": 0.01         // Deve essere multiplo di
}

Vincoli per Array

{
  "type": "array",
  "items": { "type": "string" },  // Schema per ogni elemento
  "minItems": 1,                   // Minimo elementi
  "maxItems": 10,                  // Massimo elementi
  "uniqueItems": true              // Elementi unici
}

// Array con elementi tipizzati per posizione (tuple)
{
  "type": "array",
  "prefixItems": [
    { "type": "string" },  // primo elemento
    { "type": "number" }   // secondo elemento
  ],
  "items": false  // non altri elementi
}

Vincoli per Oggetti

{
  "type": "object",
  "properties": {
    "name": { "type": "string" },
    "age": { "type": "integer" }
  },
  "required": ["name"],              // Proprietà obbligatorie
  "additionalProperties": false,     // Blocca proprietà extra
  "minProperties": 1,                // Minimo proprietà
  "maxProperties": 10                // Massimo proprietà
}

Composizione di Schema

$ref per Riutilizzo

// definitions.schema.json
{
  "$defs": {
    "address": {
      "type": "object",
      "properties": {
        "street": { "type": "string" },
        "city": { "type": "string" },
        "country": { "type": "string" }
      }
    }
  }
}

// user.schema.json
{
  "type": "object",
  "properties": {
    "name": { "type": "string" },
    "address": { "$ref": "#/$defs/address" },
    "billingAddress": { "$ref": "#/$defs/address" }
  }
}

allOf, anyOf, oneOf

// allOf: deve soddisfare TUTTI gli schema
{
  "allOf": [
    { "$ref": "#/$defs/baseUser" },
    { "properties": { "role": { "const": "admin" } } }
  ]
}

// anyOf: deve soddisfare ALMENO UNO
{
  "anyOf": [
    { "type": "string" },
    { "type": "number" }
  ]
}

// oneOf: deve soddisfare ESATTAMENTE UNO
{
  "oneOf": [
    { "properties": { "type": { "const": "email" }, "email": { "type": "string" } } },
    { "properties": { "type": { "const": "sms" }, "phone": { "type": "string" } } }
  ]
}

Condizionali: if/then/else

{
  "type": "object",
  "properties": {
    "paymentMethod": { "enum": ["card", "bank"] }
  },
  "if": {
    "properties": { "paymentMethod": { "const": "card" } }
  },
  "then": {
    "properties": {
      "cardNumber": { "type": "string", "pattern": "^[0-9]{16}$" }
    },
    "required": ["cardNumber"]
  },
  "else": {
    "properties": {
      "iban": { "type": "string", "pattern": "^[A-Z]{2}[0-9]{2}[A-Z0-9]{1,30}$" }
    },
    "required": ["iban"]
  }
}

Integrazione con OpenAPI

# openapi.yaml
openapi: 3.1.0
info:
  title: User API
  version: 1.0.0

paths:
  /users:
    post:
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateUser'
      responses:
        '201':
          description: User created
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/User'

components:
  schemas:
    CreateUser:
      type: object
      properties:
        name:
          type: string
          minLength: 1
        email:
          type: string
          format: email
      required: [name, email]

    User:
      allOf:
        - $ref: '#/components/schemas/CreateUser'
        - type: object
          properties:
            id:
              type: integer
          required: [id]

Validazione in JavaScript

// Con Ajv (Another JSON Validator)
import Ajv from 'ajv';
import addFormats from 'ajv-formats';

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

const schema = {
  type: 'object',
  properties: {
    name: { type: 'string', minLength: 1 },
    email: { type: 'string', format: 'email' },
    age: { type: 'integer', minimum: 0 }
  },
  required: ['name', 'email']
};

const validate = ajv.compile(schema);

const data = { name: 'Mario', email: 'mario@example.com', age: 30 };
const valid = validate(data);

if (!valid) {
  console.log(validate.errors);
  // [{ keyword: 'required', params: { missingProperty: 'email' }, ... }]
}

Validazione in Python

from jsonschema import validate, ValidationError

schema = {
    "type": "object",
    "properties": {
        "name": {"type": "string", "minLength": 1},
        "email": {"type": "string", "format": "email"},
        "age": {"type": "integer", "minimum": 0}
    },
    "required": ["name", "email"]
}

data = {"name": "Mario", "email": "mario@example.com", "age": 30}

try:
    validate(instance=data, schema=schema)
    print("Valid!")
except ValidationError as e:
    print(f"Invalid: {e.message}")

Best Practices

Organizzazione Schema

  • Un file per schema: Mantieni schema separati e riferiscili con $ref
  • Usa $defs: Definisci componenti riutilizzabili
  • Versioning: Includi la versione nell'URL dello schema
  • Descrizioni: Aggiungi description a ogni proprietà

Validazione Robusta

// additionalProperties: false per API strette
{
  "type": "object",
  "properties": {
    "name": { "type": "string" }
  },
  "additionalProperties": false  // Rifiuta proprietà non definite
}

// propertyNames per validare i nomi delle chiavi
{
  "type": "object",
  "propertyNames": {
    "pattern": "^[a-z_]+$"  // Solo lettere minuscole e underscore
  }
}

Generare Schema Automaticamente

// Da TypeScript (typescript-json-schema)
npx typescript-json-schema tsconfig.json User --out user.schema.json

// Da dati JSON (quicktype)
npx quicktype sample.json -o schema.json --lang schema

// Da Zod (zod-to-json-schema)
import { z } from 'zod';
import { zodToJsonSchema } from 'zod-to-json-schema';

const UserSchema = z.object({
  name: z.string().min(1),
  email: z.string().email()
});

const jsonSchema = zodToJsonSchema(UserSchema);

Strumenti Correlati

Per lavorare con JSON Schema:

Conclusione

JSON Schema è essenziale per API professionali. Punti chiave:

  • Definisci schema per tutti i dati in input e output
  • Usa $ref per riutilizzare definizioni comuni
  • Integra la validazione nel middleware API
  • Genera documentazione automaticamente con OpenAPI
  • Testa gli schema con dati validi e invalidi

Investire tempo nella definizione degli schema ripaga enormemente in termini di qualità del codice e riduzione dei bug.

Per altri strumenti utili, esplora i nostri tool online gratuiti. Per la specifica completa, consulta json-schema.org.