JSON Toolkit

JSON Schema: मूल बातों से प्रोडक्शन तक

मुख्य बात: Keywords बहुत हैं, पर रोज़ केवल एक दर्जन इस्तेमाल होते हैं। type/properties/required/$ref सही कर लें, error messages अच्छे संभालें, और आप असली मामलों का अस्सी प्रतिशत कवर कर लेंगे।

मध्यम~14 मिनट पढ़ें2026-10-30 को प्रकाशित2026-10-30 को अपडेट किया गया
JSON Schema: मूल बातों से प्रोडक्शन तक का कवर

सारांश: पहले $schema को 2020-12 पर पिन करें, फिर $defs + $ref से साझा संरचनाएँ निकालें; oneOf केवल तब इस्तेमाल करें जब ठीक एक branch का मेल ज़रूरी हो, और बाकी हर जगह discriminators या anyOf को प्राथमिकता दें।

Schema दस्तावेज़ का परिशिष्ट नहीं; वह ऐसी संपत्ति है जिसे चलाया, जाँचा और जनरेट किया जा सकता है।

1. पहले version साफ़ करें

Version $schema मान स्थिति टिप्पणियाँ
draft-04 http://json-schema.org/draft-04/schema# पुराना Legacy प्रोजेक्ट्स और कुछ OpenAPI 2.0 tooling
draft-07 http://json-schema.org/draft-07/schema# व्यापक रूप से उपयोग OpenAPI 3.0 का de facto आधार
2019-09 https://json-schema.org/draft/2019-09/schema उपलब्ध $defs और annotation vocabulary पेश किए
2020-12 https://json-schema.org/draft/2020-12/schema वर्तमान में अनुशंसित prefixItems पेश किया, items की semantics बदलीं

नए प्रोजेक्ट्स के लिए 2020-12 इस्तेमाल करें। ध्यान दें कि 2020-12 में items केवल "शेष elements" को सीमित करता है — tuple-शैली declarations prefixItems में चले गए। draft-07 से migrate करते समय यह सबसे आम जाल है।

2. एक न्यूनतम उपयोगी schema

{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://bbdrug.com/schemas/user.json",
  "type": "object",
  "required": ["id", "name"],
  "properties": {
    "id": { "type": "string", "minLength": 1 },
    "name": { "type": "string", "minLength": 1, "maxLength": 64 },
    "age": { "type": "integer", "minimum": 0, "maximum": 150 },
    "tags": { "type": "array", "items": { "type": "string" }, "uniqueItems": true }
  },
  "additionalProperties": false
}

यह schema चार सबसे आम चिंताएँ कवर करता है: type बाधाएँ, required फ़ील्ड, range सीमाएँ और array element बाधाएँ।

3. Keyword नक्शा

3.1 सामान्य

Keyword उद्देश्य किस पर ध्यान दें
type Type; array हो सकता है "type": ["string", "null"] का अर्थ nullable
enum Enumerated मान मान अद्वितीय होने चाहिए
const एकल-मान बाधा version number जैसे निश्चित मानों के लिए अच्छा
$ref किसी अन्य schema का संदर्भ पूरे schema का संदर्भ देता है और संदर्भ देने वाले object के sibling keywords के साथ merge नहीं होता

3.2 संख्याएँ

multipleOf, maximum, exclusiveMaximum, minimum, exclusiveMinimum. ध्यान दें कि draft-04 में exclusiveMaximum एक boolean था (जो maximum के साथ मिलकर इस्तेमाल होता था); draft-07 से ही यह स्वतंत्र संख्यात्मक keyword बना — जिससे "पुराने दस्तावेज़ों से नकल किए गए बहुत से schemas" strict mode में विफल होते हैं।

3.3 स्ट्रिंग्स

minLength, maxLength, pattern (एक ECMA-262 regular expression), और format (एक annotation)।

minLength Unicode code points गिनता है, UTF-16 units या bytes नहीं। JavaScript के string.length से validate करने पर इससे मतभेद होगा।

3.4 Arrays

Keyword उद्देश्य
prefixItems 2020-12 tuple-शैली बाधा, index के अनुसार items एक-एक करके मिलाती है
items शेष elements पर एकसमान बाधा (2020-12)
contains + minContains/maxContains कम-से-कम/अधिक-से-अधिक कितने मेल खाते items मौजूद हों
uniqueItems Elements सब अलग होने चाहिए (deep comparison; बड़े arrays पर बचें)
minItems / maxItems लंबाई बाधाएँ

3.5 Objects

properties, required, patternProperties, additionalProperties, propertyNames, minProperties, maxProperties, dependentRequired, dependentSchemas.

तीन उच्च-आवृत्ति भ्रांतियाँ:

  1. required केवल key का मौजूद होना चाहता है: {"name": ""} मान्य है। खाली स्ट्रिंग अस्वीकार करने के लिए minLength: 1 जोड़ना होगा;
  2. additionalProperties: false compatibility समस्याएँ बढ़ा देता है: client का एक अतिरिक्त फ़ील्ड पूरा payload विफल कर देता है, इसलिए API विकसित करने पर हर caller को एक साथ upgrade करना पड़ता है;
  3. properties undeclared keys को सीमित नहीं करता — डिफ़ॉल्ट रूप से वे अनुमत हैं।

3.6 Logical composition

{
  "oneOf": [
    { "type": "object", "required": ["cardNumber"], "properties": { "cardNumber": { "type": "string" } } },
    { "type": "object", "required": ["accountId"], "properties": { "accountId": { "type": "string" } } }
  ]
}
Keyword Semantics विशिष्ट विफलता
allOf सभी को संतुष्ट होना चाहिए कई branches एक ही फ़ील्ड पर परस्पर विरोधी बाधाएँ लगाती हैं
anyOf कम से कम एक को संतुष्ट होना चाहिए Error message को विशिष्ट branch तक पहुँचाना कठिन
oneOf ठीक एक को संतुष्ट होना चाहिए डेटा दो branches से मेल खाता है और विफल हो जाता है, भले ही वह "स्पष्ट रूप से सही दिखता है"
not संतुष्ट नहीं होना चाहिए कम इस्तेमाल, और सबसे कम मित्रवत error messages

oneOf वह keyword है जो सबसे अधिक "मान्य डेटा जो validation में विफल होता है" पैदा करता है। अगर branch बाधाएँ overlap करती हैं (उदाहरण के लिए दोनों अतिरिक्त फ़ील्ड की अनुमति देती हैं), तो एक record कई branches से मेल खाता है और oneOf सीधे विफल हो जाता है। उस स्थिति में anyOf पर switch करें, या branches के अंदर const के साथ discriminator फ़ील्ड इस्तेमाल करें।

{
  "type": "object",
  "required": ["kind"],
  "properties": { "kind": { "enum": ["text", "image"] } },
  "if": { "properties": { "kind": { "const": "image" } } },
  "then": { "required": ["url", "width", "height"] },
  "else": { "required": ["content"] }
}

Discriminator शैली oneOf से अधिक पठनीय है, और बेहतर errors देती है।

4. बाँटना और दोबारा उपयोग

{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://bbdrug.com/schemas/order.json",
  "$defs": {
    "money": { "type": "string", "pattern": "^-?\\d+\\.\\d{2}$" },
    "address": {
      "type": "object",
      "required": ["city"],
      "properties": { "city": { "type": "string" }, "detail": { "type": "string" } }
    }
  },
  "type": "object",
  "required": ["total", "shipTo"],
  "properties": {
    "total": { "$ref": "#/$defs/money" },
    "shipTo": { "$ref": "#/$defs/address" }
  }
}
  • $defs 2019-09 में पेश किया गया रूप है; draft-07 definitions इस्तेमाल करता है;
  • Cross-file references सापेक्ष $ids इस्तेमाल करते हैं, लेकिन प्रोडक्शन में runtime पर remote schemas fetch करने पर निर्भर न रहें — इन्हें स्थिर फ़ाइलों के रूप में bundle करें;
  • आप संदर्भ के साथ-साथ बाधाएँ नहीं जोड़ सकते (sibling keywords के साथ $ref का व्यवहार version के अनुसार बदलता है)। Composition चाहिए तो allOf इस्तेमाल करें।

5. Ajv से runtime validation

import Ajv2020 from 'ajv/dist/2020';
import addFormats from 'ajv-formats';
import orderSchema from './order.json' with { type: 'json' };

const ajv = new Ajv2020({
  allErrors: true,
  strict: true,
  allowUnionTypes: true,
});
addFormats(ajv);

const validateOrder = ajv.compile(orderSchema);

export function assertOrder(input: unknown): void {
  if (validateOrder(input)) {
    return;
  }
  const detail = (validateOrder.errors ?? []).map((errorItem) => ({
    path: errorItem.instancePath || '/',
    keyword: errorItem.keyword,
    message: errorItem.message,
    params: errorItem.params,
  }));
  throw new Error(`Order data failed validation: ${JSON.stringify(detail)}`);
}

मुख्य बिंदु:

  • compile केवल एक बार कॉल करें, module top level पर; हर request पर दोबारा compile करना 10 ms की validation को सैकड़ों milliseconds में बदल देता है;
  • strict: true गलत लिखे keywords जल्दी उजागर करता है (जैसे minLength के बजाय minlength), इसलिए इसे चालू करना इसके लायक है;
  • केवल ajv-formats से format: "email" और format: "date-time" वास्तव में प्रभावी होते हैं;
  • 2020-12 को ajv/dist/2020 entry point चाहिए — डिफ़ॉल्ट export इस्तेमाल न करें।

6. Error messages कैसे design करें

Validator जो देता है वह है "कौन-सा keyword विफल हुआ"; उपयोगकर्ता जो चाहता है वह है "कौन-सा input box गलत है"। एक mapping परत जोड़ना इसके लायक है:

const FIELD_LABEL: Record<string, string> = {
  '/name': 'Name',
  '/age': 'Age',
  '/tags': 'Tags',
};

/**
 * Ajv errors को प्रदर्शन-योग्य संदेशों में बदलें।
 * @param errors Ajv errors की सूची
 * @returns प्रदर्शन संदेशों का array
 */
function toMessages(errors: Array<{ instancePath: string; keyword: string; params: unknown }>): string[] {
  return errors.map((errorItem) => {
    const label = FIELD_LABEL[errorItem.instancePath] ?? errorItem.instancePath;
    if (errorItem.keyword === 'required') {
      const missing = (errorItem.params as { missingProperty: string }).missingProperty;
      return `${label} is missing the required field ${missing}`;
    }
    if (errorItem.keyword === 'minLength') {
      return `${label} is too short`;
    }
    return `${label} failed validation (${errorItem.keyword})`;
  });
}

API पक्ष पर machine-readable संरचना रखें ताकि front end तय कर सके कि इसे कैसे प्रस्तुत करना है:

{
  "type": "https://bbdrug.com/problems/validation",
  "title": "Request data validation failed",
  "status": 422,
  "errors": [
    { "path": "/items/0/price", "keyword": "pattern", "message": "Must be a monetary string with two decimal places" }
  ]
}

7. Performance और जाल

  • Validation लागत schema जटिलता और डेटा आकार के साथ बढ़ती है; कई oneOf branches combinatorial explosion पैदा करते हैं, जो हज़ारों records के साथ बहुत स्पष्ट हो जाता है;
  • uniqueItems array पर O(n²) deep comparison करता है; बड़े arrays के लिए business layer में deduplicate करें;
  • pattern में catastrophic backtracking वाले पैटर्न (जैसे (a+)+$) से बचें; यह ReDoS का क्लासिक प्रवेश द्वार है;
  • जब additionalProperties: false allOf के साथ मिलाया जाता है, तो subschema में घोषित फ़ील्ड parent द्वारा "additional" माने जाते हैं, इसलिए आपको उन्हें एक ही schema में merge करना होगा या unevaluatedProperties इस्तेमाल करना होगा।

8. Checklist

  • [ ] 2020-12 पिन करें और फ़ाइल के शीर्ष पर $schema घोषित करें;
  • [ ] साझा संरचनाएँ $defs + $ref से निकालें, copy-paste से नहीं;
  • [ ] oneOf के बजाय discriminators को प्राथमिकता दें;
  • [ ] Validator के compile परिणाम का दोबारा उपयोग करें और strict mode चालू करें;
  • [ ] स्पष्ट रूप से दस्तावेज़ित करें कि format लागू होता है या नहीं;
  • [ ] Error responses path, keyword और उपयोगकर्ता-मुखी संदेश देते हैं;
  • [ ] Schema को CI में रखें: JSON Schema Test Suite cases को regression tests के रूप में चलाएँ।

FAQ

मुझे कौन-सा JSON Schema draft चुनना चाहिए?

नए प्रोजेक्ट्स के लिए 2020-12 इस्तेमाल करें और फ़ाइल के शीर्ष पर $schema घोषित करें। अगर schema OpenAPI 3.1 में जा रहा है, तो उसे भी 2020-12 होना चाहिए, क्योंकि 3.1 सीधे वही version embed करता है; draft-07 केवल उन प्रोजेक्ट्स के लिए रखें जो अभी भी पुरानी toolchains से बँधे हैं। items और $ref sibling keywords पर दोनों का व्यवहार अलग है, इसलिए एक schema समूह में इन्हें न मिलाएँ।

additionalProperties: false compatibility समस्याएँ क्यों बढ़ा देता है?

यह हर undeclared key को error मानता है, इसलिए client का एक अतिरिक्त फ़ील्ड पूरा payload विफल कर देता है। फिर API विकसित होते समय हर caller को एक साथ upgrade करना पड़ता है, वरना पुराने clients अस्वीकार हो जाते हैं। Config-शैली schemas को अतिरिक्त फ़ील्ड स्पष्ट रूप से बंद करने चाहिए, जबकि सार्वजनिक APIs को compatibility लागत तौलनी चाहिए।

क्या मैं oneOf या anyOf इस्तेमाल करूँ?

oneOf केवल तब इस्तेमाल करें जब डेटा को ठीक एक branch से मेल खाना ही हो। अगर branch बाधाएँ एक साथ पूरी हो सकती हैं, तो एक record कई branches से मेल खाता है और oneOf सीधे विफल हो जाता है; anyOf पर switch करें, या हर branch के अंदर const discriminator जोड़ें।

संदर्भ

संबंधित लेख

Schema और TypeScript Types के बीच दो-तरफ़ा Sync

Types और schemas में से हर एक एक हिस्सा कवर करता है: एक compile time पर, एक runtime पर। सवाल यह नहीं कि किसे चुनें, बल्कि यह कि दोनों को स्वतः sync में कैसे रखें।

मध्यम~11 मिनट पढ़ें2026-10-30 को अपडेट किया गया

JSON API डेटा कॉन्ट्रैक्ट डिज़ाइन के मूल बिंदु

छह चीज़ें ऐसी हैं जिन्हें जारी होने के बाद बदलना बहुत कठिन है: प्रतिक्रिया एनवेलप, त्रुटि फ़ॉर्मेट, पेजिनेशन आकार, ID और समय का प्रतिनिधित्व, null बनाम अनुपस्थित का अर्थ, और वर्ज़न विकास।

मध्यम~13 मिनट पढ़ें2026-10-30 को अपडेट किया गया

additionalProperties से ऑब्जेक्ट बंद न करें तो Schema वैलिडेशन बेमानी है

वैलिडेशन पास होना साफ़ डेटा की गारंटी नहीं है। Pydantic डिफ़ॉल्ट रूप से अनजान फ़ील्ड को नज़रअंदाज़ करता है, Zod उन्हें हटा देता है, Ajv छोड़ देता है — झूठे पैरामीटर और टाइपो कुंजियाँ चुपचाप निकल जाती हैं। यह लेख मौन स्वीकृति के तीन रूप दिखाता है और पूरे तरीके देता है: ऑब्जेक्ट बंद करना, कोअर्सन नियंत्रित करना, हर नेस्टिंग स्तर कसना।

Intermediate~8 मिनट पढ़ें2026-11-02 को अपडेट किया गया