JSON Toolkit

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

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

मध्यम~13 मिनट पढ़ें2026-10-30 को प्रकाशित2026-10-30 को अपडेट किया गया
JSON API डेटा कॉन्ट्रैक्ट डिज़ाइन के मूल बिंदुओं का कवर

मुख्य निष्कर्ष: पेजिनेशन और नामकरण के विवरण पर बहस करने से पहले कॉन्ट्रैक्ट में प्रतिक्रिया एनवेलप, त्रुटि फ़ॉर्मेट, ID और समय का प्रतिनिधित्व, तथा null-बनाम-अनुपस्थित अर्थ को तय करें; शीर्ष स्तर को हमेशा data एनवेलप में लपेटें ताकि पेजिनेशन और ट्रेसिंग ID बाद में मौजूद कॉलर को तोड़े बिना जोड़े जा सकें।

API डिज़ाइन में कोई मानक उत्तर नहीं हैं, लेकिन कुछ ऐसे निर्णय हैं जिन्हें चुनने के बाद वापस लेना कठिन होता है — कोड की पहली पंक्ति लिखने से पहले उन पर सोच लेना बेहतर है।

1. प्रतिक्रिया को एनवेलप करें या नहीं

तीन शैलियाँ:

// शैली A: नंगी प्रतिक्रिया
{ "id": 1, "name": "张三" }

// शैली B: data एनवेलप
{ "data": { "id": 1, "name": "张三" } }

// शैली C: data + मेटाडेटा
{ "data": { "id": 1, "name": "张三" }, "meta": { "requestId": "a1b2" } }
शैली फ़ायदे नुकसान
नंगी प्रतिक्रिया सबसे सरल, फ्रंटएंड के लिए एक परत कम मेटाडेटा नहीं जोड़ सकते; शीर्ष-स्तरीय ऐरे में JSON हाईजैकिंग और विकास जोखिम
data एनवेलप meta / errors / links जोड़ना आसान एक और नेस्टिंग स्तर, फ्रंटएंड को res.data.data लिखना पड़ता है
data + मेटाडेटा सर्वोत्तम अवलोकनीयता meta में कौन-से फ़ील्ड जाएँ, इस पर सख़्त सहमति चाहिए

सुझाव: सूचियों और विवरण दोनों के लिए समान रूप से data एनवेलप का उपयोग करें, और शीर्ष स्तर को कभी ऐरे न बनाएँ। इस तरह, बाद में पेजिनेशन या ट्रेसिंग ID जोड़ने से कॉन्ट्रैक्ट कभी नहीं टूटता।

2. त्रुटि फ़ॉर्मेट: RFC 9457 की नकल करें

अपनी खुद की त्रुटि संरचना ईजाद करना सहयोग लागत का बहुत बड़ा स्रोत है। RFC 9457 (जो RFC 7807 को प्रतिस्थापित करता है) application/problem+json को परिभाषित करता है:

{
  "type": "https://bbdrug.com/problems/insufficient-balance",
  "title": "余额不足",
  "status": 422,
  "detail": "账户余额 12.00 元,本次扣款需要 30.00 元",
  "instance": "/accounts/A1001/payments/p-889",
  "balance": "12.00",
  "required": "30.00"
}
फ़ील्ड अर्थ आवश्यक
type स्थिर problem-type URI अनुशंसित
title छोटा मानव-पठनीय सारांश अनुशंसित
status HTTP स्टेटस कोड अनुशंसित
detail इस बार क्या हुआ, इसकी विशिष्ट व्याख्या वैकल्पिक
instance वह संसाधन इंस्टेंस जिसमें समस्या हुई वैकल्पिक
एक्सटेंशन फ़ील्ड मनमाना व्यावसायिक फ़ील्ड वैकल्पिक

एक्सटेंशन फ़ील्ड (जैसे ऊपर balance / required) स्पेक द्वारा अनुमत हैं, और फ्रंटएंड उन्हें सटीक संदेश बनाने के लिए उपयोग कर सकता है, detail टेक्स्ट को पार्स करने के बजाय।

3. पेजिनेशन: cursor offset से बेहतर

// Offset पेजिनेशन
{ "data": [], "page": { "offset": 40, "limit": 20, "total": 137 } }

// Cursor पेजिनेशन
{ "data": [], "page": { "nextCursor": "eyJpZCI6MTAwfQ", "hasMore": true } }
आयाम Offset पेजिनेशन Cursor पेजिनेशन
कार्यान्वयन कठिनाई कम (LIMIT/OFFSET) मध्यम (स्थिर सॉर्ट कुंजी चाहिए)
गहरे पेजों पर प्रदर्शन ख़राब (offset जितना बड़ा, उतना धीमा) अच्छा
डेटा बदलने पर डुप्लिकेट या गायब आइटम स्थिर
किसी पेज पर जंप संभव संभव नहीं
कुल गिनती चाहिए अतिरिक्त count चाहिए आवश्यक नहीं

सुझाव: एडमिन तालिकाओं के लिए offset पेजिनेशन (उन्हें पेज जंप और कुल चाहिए), और उपयोगकर्ता-मुखी इन्फ़िनिट फ़ीड के लिए cursor पेजिनेशन।

4. ID और समय

4.1 ID के लिए हमेशा स्ट्रिंग्स का उपयोग करें

तर्क प्रकारों वाले लेख में समझाया गया था: JavaScript पक्ष पर, 2^53 से ऊपर के मान चुपचाप परिशुद्धता खो देते हैं। भले ही वर्तमान ID छोटा स्वतः-वृद्धि पूर्णांक हो, स्ट्रिंग्स का उपयोग बाद में snowflake ID पर स्विच करने की गुंजाइश छोड़ता है।

{ "id": "1000000000000000001" }

4.2 समय के लिए RFC 3339 स्ट्रिंग्स का उपयोग करें

{
  "createdAt": "2026-10-30T10:41:45+08:00",
  "updatedAt": "2026-10-30T02:41:45Z"
}
  • मिलीसेकंड टाइमस्टैम्प संख्याओं का उपयोग न करें: वे पढ़ने में कठिन हैं, और इकाई (सेकंड/मिलीसेकंड) अक्सर भ्रमित होती है;
  • समय क्षेत्र स्पष्ट रूप से निर्दिष्ट करें: या Z के साथ या ऑफ़सेट के साथ; "बिना समय क्षेत्र के स्थानीय समय" कभी न उपयोग करें;
  • फ़ील्ड का नाम समान रूप से xxxAt रखें ताकि एक नज़र में स्पष्ट हो कि यह समय का एक बिंदु है।

5. null और अनुपस्थित

आपको एक चुनकर दस्तावेज़ में लिखना होगा:

दृष्टिकोण "कोई मान नहीं" व्यक्त करना "संशोधित न करें" व्यक्त करना
दृष्टिकोण A फ़ील्ड का मान null है अनुरोध में फ़ील्ड छोड़ें
दृष्टिकोण B फ़ील्ड मौजूद नहीं है अनुरोध में फ़ील्ड छोड़ें

दृष्टिकोण A की समस्या यह है कि PATCH में "छोड़ा गया" और "null के रूप में भेजा गया" अलग अर्थ रखते हैं, जिन्हें ग़लत समझना आसान है; दृष्टिकोण B की समस्या यह है कि फ्रंटएंड का JSON.stringify उन फ़ील्ड्स को गिरा देता है जिनका मान undefined है, जो संयोगवश अर्थ से मेल खाता है।

व्यावहारिक सुझाव:

  • प्रतिक्रिया बॉडी के लिए दृष्टिकोण A का उपयोग करें (null स्पष्ट रूप से कोई मान नहीं दर्शाता, और फ्रंटएंड को undefined अलग करने की ज़रूरत नहीं);
  • अनुरोध बॉडी (विशेष रूप से PATCH) के लिए दृष्टिकोण B का उपयोग करें, Merge Patch या JSON Patch के साथ मिलाकर अर्थ स्पष्ट करें।

6. फ़ील्ड नामकरण और संरचना

निर्णय सामान्य विकल्प चेतावनी
नामकरण शैली camelCase (JS इकोसिस्टम) / snake_case (Python, डेटाबेस) प्रोजेक्ट में समान रखें, न मिलाएँ
enum मान लोअरकेस अंडरस्कोर के साथ, "pending_review" संख्याओं का उपयोग न करें; वे पढ़ने में कठिन हैं
बूलियन फ़ील्ड isActive / enabled status: 0/1 का उपयोग न करें
ऐरे फ़ील्ड बहुवचन संज्ञा, items खाली ऐरे के लिए [] लौटाएँ, कभी null नहीं
नेस्टिंग गहराई 3 स्तरों से अधिक नहीं उससे गहरा हो तो विभाजित करें या expand पैरामीटर दें

खाली ऐरे के लिए null के बजाय [] लौटाना बहुत मायने रखता है: फ्रंटएंड पर data.items.map(...) किसी null के कारण क्रैश नहीं होगा।

7. विकास रणनीति

परिवर्तन संगतता तोड़ता है कैसे संभालें
वैकल्पिक फ़ील्ड जोड़ें नहीं सीधे भेजें
अनिवार्य फ़ील्ड जोड़ें हाँ डिफ़ॉल्ट मान जोड़ें, या वर्ज़न से जारी करें
फ़ील्ड हटाएँ हाँ पहले deprecated चिह्नित करें, कॉलर देखें, फिर हटाएँ
फ़ील्ड का प्रकार बदलें हाँ नया फ़ील्ड नाम जोड़ें और संक्रमण के दौरान dual-write करें
enum मान समूह बदलें निर्भर क्लाइंट के पास अज्ञात मानों के लिए fallback हो
फ़ील्ड का अर्थ बदलें हाँ इसे नया फ़ील्ड मानें

अनुशासन के तीन नियम:

  1. क्लाइंट अज्ञात फ़ील्ड के प्रति सहिष्णु हों (additionalProperties: false से खुद को न घेरें);
  2. क्लाइंट के पास अज्ञात enum मानों के लिए fallback शाखा हो;
  3. deprecated फ़ील्ड कम से कम एक major वर्ज़न तक रखें, और उन्हें प्रतिक्रिया हेडर या दस्तावेज़ में Deprecation से चिह्नित करें।

8. एक पूरा उदाहरण

{
  "data": [
    {
      "id": "1001",
      "name": "JSON 权威指南",
      "price": "89.50",
      "tags": ["json", "spec"],
      "publishedAt": "2026-09-01T00:00:00+08:00",
      "author": null
    }
  ],
  "page": { "nextCursor": null, "hasMore": false },
  "meta": { "requestId": "req-7f3a", "tookMs": 12 }
}

यह प्रतिक्रिया ऊपर के सभी सम्मेलनों को दर्शाती है: data एनवेलप, स्ट्रिंग ID, राशियाँ स्ट्रिंग के रूप में, समय क्षेत्र के साथ टाइमस्टैम्प, null का अर्थ अनुपस्थित, खाली ऐरे, cursor पेजिनेशन, और अवलोकनीयता जानकारी के लिए meta।

9. चेकलिस्ट

  • [ ] शीर्ष स्तर ऐरे नहीं है; समान रूप से data एनवेलप का उपयोग करें;
  • [ ] त्रुटि प्रतिक्रियाएँ RFC 9457 का पालन करती हैं और स्थिर type शामिल करती हैं;
  • [ ] पेजिनेशन आकार व्यावसायिक परिदृश्य से मेल खाता है;
  • [ ] ID स्ट्रिंग हैं, और समय RFC 3339 का उपयोग करता है;
  • [ ] null और अनुपस्थित का अर्थ दस्तावेज़ में लिखा गया है;
  • [ ] नामकरण शैली प्रोजेक्ट में समान है, और enum लोअरकेस स्ट्रिंग्स का उपयोग करते हैं;
  • [ ] क्लाइंट के पास अज्ञात फ़ील्ड और अज्ञात enum मानों के लिए fallback है;
  • [ ] ब्रेकिंग परिवर्तन वर्ज़निंग या dual-write संक्रमण से गुज़रते हैं।

FAQ

मैं विकसन-योग्य JSON API कैसे डिज़ाइन करूँ?

सूचियों और विवरणों को समान रूप से data एनवेलप में लपेटें और शीर्ष स्तर को कभी ऐरे न बनाएँ, ताकि पेजिनेशन और ट्रेसिंग ID कॉन्ट्रैक्ट तोड़े बिना जोड़े जा सकें। स्ट्रिंग ID और RFC 3339 टाइमस्टैम्प का उपयोग करें, और null-बनाम-अनुपस्थित अर्थ को दस्तावेज़ में लिखें। नए फ़ील्ड वैकल्पिक और अर्थ में स्वतंत्र रखें, और क्लाइंट को अज्ञात फ़ील्ड और enum मानों के लिए fallback दें।

अपना कस्टम त्रुटि फ़ॉर्मेट क्यों न ईजाद करें?

अपनी खुद की त्रुटि संरचना ईजाद करना सहयोग लागत का बहुत बड़ा स्रोत है। RFC 9457 के application/problem+json की नकल करें, समस्याओं को स्थिर type से पहचानें, और balance व required जैसे एक्सटेंशन फ़ील्ड की अनुमति दें, ताकि फ्रंटएंड detail टेक्स्ट पार्स किए बिना सटीक संदेश बना सके।

क्या हर endpoint को cursor पेजिनेशन उपयोग करना चाहिए?

नहीं। एडमिन तालिकाओं को पेज जंप और कुल गिनती चाहिए, इसलिए offset पेजिनेशन बेहतर फ़िट होता है; उपयोगकर्ता-मुखी इन्फ़िनिट फ़ीड को cursor पेजिनेशन उपयोग करना चाहिए, जो गहरे पेजों पर स्थिर रहता है और डेटा बदलने पर डुप्लिकेट या अंतराल से बचता है। परिदृश्य को पेज जंप और कुल गिनती चाहिए या नहीं, इस आधार पर चुनें।