JSON Toolkit

JSON API ডেটা চুক্তি নকশার মূল বিষয়

সংক্ষেপে: প্রকাশের পর বদলানো খুব কঠিন এমন ছয়টি জিনিস: রেসপন্স এনভেলপ, এরর ফরম্যাট, পেজিনেশনের আকৃতি, ID ও সময়ের উপস্থাপন, null বনাম অনুপস্থিতের সেমান্টিক্স, আর সংস্করণ বিবর্তন।

মধ্যবর্তী~13 মিনিট পড়াপ্রকাশ 2026-10-30আপডেট 2026-10-30
JSON API ডেটা চুক্তি নকশার মূল বিষয়ের কভার

মূল কথা: পেজিনেশন ও নামকরণের খুঁটিনাটি নিয়ে তর্কের আগে রেসপন্স এনভেলপ, এরর ফরম্যাট, ID ও সময়ের উপস্থাপন, এবং null বনাম অনুপস্থিতের সেমান্টিক্স চুক্তিতে লক করুন; টপ লেভেল সবসময় data এনভেলপে মুড়ুন যাতে পরে পেজিনেশন ও ট্রেসিং ID যোগ করলে বিদ্যমান কলার না ভাঙে।

API নকশায় কোনো মান উত্তর নেই, কিন্তু কিছু "একবার বাছলে ফেরানো কঠিন" সিদ্ধান্ত আছে যা কোডের প্রথম লাইন লেখার আগেই ভাবা ভালো।

১. রেসপন্স এনভেলপ করবেন কি না

তিন শৈলী:

// Style A: bare response
{ "id": 1, "name": "করিম" }

// Style B: data envelope
{ "data": { "id": 1, "name": "করিম" } }

// Style C: data + metadata
{ "data": { "id": 1, "name": "করিম" }, "meta": { "requestId": "a1b2" } }
শৈলী সুবিধা অসুবিধা
খালি রেসপন্স সবচেয়ে সরল, ফ্রন্টএন্ডে এক স্তর কম মেটাডেটা জোড়া লাগানো যায় না; টপ-লেভেল অ্যারেতে JSON হাইজ্যাকিং ও বিবর্তনের ঝুঁকি
ডেটা এনভেলপ meta / errors / links যোগ করা সহজ আরেক স্তরের নেস্টিং, ফ্রন্টএন্ডে res.data.data লিখতে হয়
ডেটা + মেটাডেটা সেরা অবজারভেবিলিটি কোন ফিল্ড meta-তে থাকবে তা কঠোরভাবে চুক্তি করতে হয়

সুপারিশ: লিস্ট ও ডিটেইল — দুই ক্ষেত্রেই সমানভাবে data এনভেলপ ব্যবহার করুন, আর টপ লেভেল কখনো অ্যারে করবেন না। তাতে পরে পেজিনেশন বা ট্রেসিং ID যোগ করলে চুক্তি কখনো ভাঙে না।

২. এরর ফরম্যাট: 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 স্থিতিশীল সমস্যা-টাইপ URI সুপারিশকৃত
title সংক্ষিপ্ত মানুষ-পাঠযোগ্য সারসংক্ষেপ সুপারিশকৃত
status HTTP স্টেটাস কোড সুপারিশকৃত
detail এবার ঠিক কী ঘটেছে তার নির্দিষ্ট ব্যাখ্যা ঐচ্ছিক
instance যে রিসোর্স ইনস্ট্যান্সে সমস্যা হয়েছে ঐচ্ছিক
এক্সটেনশন ফিল্ড যেকোনো ব্যবসায়িক ফিল্ড ঐচ্ছিক

এক্সটেনশন ফিল্ড (উপরের balance / required-এর মতো) স্পেসিফিকেশনে অনুমোদিত, আর ফ্রন্টএন্ড detail টেক্সট পার্স না করে নির্দিষ্ট বার্তা বানাতে পারে।

৩. পেজিনেশন: cursor offset-কে হারায়

// Offset pagination
{ "data": [], "page": { "offset": 40, "limit": 20, "total": 137 } }

// Cursor pagination
{ "data": [], "page": { "nextCursor": "eyJpZCI6MTAwfQ", "hasMore": true } }
মাত্রা Offset পেজিনেশন Cursor পেজিনেশন
বাস্তবায়নের কঠিনতা কম (LIMIT/OFFSET) মাঝারি (স্থিতিশীল সর্ট কি দরকার)
গভীর পেজে পারফরম্যান্স দুর্বল (offset যত বড়, তত ধীর) ভালো
ডেটা বদলালে ডুপ্লিকেট বা বাদ পড়া আইটেম স্থিতিশীল
নির্দিষ্ট পেজে লাফ সম্ভব সম্ভব নয়
মোট গণনা দরকার অতিরিক্ত count দরকার দরকার নেই

সুপারিশ: অ্যাডমিন টেবিলে offset পেজিনেশন (তাদের পেজ-লাফ ও মোট দরকার), আর ব্যবহারকারীর infinite ফিডে cursor পেজিনেশন।

৪. ID ও সময়

৪.১ ID-তে সবসময় স্ট্রিং

যুক্তি টাইপের নিবন্ধে আলোচিত: JavaScript দিকে 2^53-এর বেশি মান নীরবে নির্ভুলতা হারায়। বর্তমান ID ছোট অটো-ইনক্রিমেন্ট ইন্টিজার হলেও স্ট্রিং ব্যবহার করলে পরে snowflake ID-তে যাওয়ার জায়গা থাকে।

{ "id": "1000000000000000001" }

৪.২ সময়ে RFC 3339 স্ট্রিং

{
  "createdAt": "2026-10-30T10:41:45+08:00",
  "updatedAt": "2026-10-30T02:41:45Z"
}
  • মিলিসেকেন্ড টাইমস্ট্যাম্প সংখ্যা ব্যবহার করবেন না: পড়া কঠিন, আর একক (সেকেন্ড/মিলিসেকেন্ড) নিয়ে ঘন ঘন ভুল হয়;
  • টাইম জোন স্পষ্ট করুন: হয় Z, নয় অফসেট দিয়ে; কখনো "টাইম জোন ছাড়া লোকাল সময়" নয়;
  • ফিল্ডের নাম সমানভাবে xxxAt রাখুন, যাতে এক নজরে বোঝা যায় এটি একটি সময়বিন্দু।

৫. null ও অনুপস্থিত

একটি বেছে ডকুমেন্টেশনে লিখতে হবে:

পদ্ধতি "কোনো মান নেই" প্রকাশ "পরিবর্তন করবেন না" প্রকাশ
পদ্ধতি A ফিল্ডের মান null রিকোয়েস্টে ফিল্ড বাদ দিন
পদ্ধতি B ফিল্ড নেই রিকোয়েস্টে ফিল্ড বাদ দিন

পদ্ধতি A-র সমস্যা: PATCH-এ "বাদ দেওয়া" ও "null পাঠানো" ভিন্ন অর্থ, যা ভুল ব্যাখ্যা করা সহজ; পদ্ধতি B-র সমস্যা: ফ্রন্টএন্ডের JSON.stringify যে ফিল্ডের মান undefined সেগুলো ফেলে দেয়, যা দৈবক্রমে সেমান্টিক্সের সঙ্গে মেলে।

ব্যবহারিক সুপারিশ:

  • রেসপন্স বডিতে পদ্ধতি A (null স্পষ্টভাবে কোনো মান নেই বোঝায়, আর ফ্রন্টএন্ডকে undefined আলাদা করতে হয় না);
  • রিকোয়েস্ট বডিতে (বিশেষত PATCH) পদ্ধতি B, Merge Patch বা JSON Patch-এর সঙ্গে মিলিয়ে সেমান্টিক্স স্পষ্ট করুন।

৬. ফিল্ড নামকরণ ও কাঠামো

সিদ্ধান্ত সাধারণ পছন্দ সতর্কতা
নামকরণ শৈলী camelCase (JS ইকোসিস্টেম) / snake_case (Python, ডেটাবেস) প্রোজেক্টজুড়ে সমান, মিশাবেন না
এনাম মান আন্ডারস্কোরসহ ছোট হাতের, "pending_review" সংখ্যা ব্যবহার করবেন না; পড়া কঠিন
বুলিয়ান ফিল্ড isActive / enabled status: 0/1 ব্যবহার করবেন না
অ্যারে ফিল্ড বহুবচন বিশেষ্য, items খালি অ্যারেতে [] ফেরত দিন, কখনো null নয়
নেস্টিং গভীরতা ৩ স্তরের বেশি নয় তার চেয়ে গভীর হলে ভাগ করুন বা expand প্যারামিটার দিন

খালি অ্যারেতে null-এর বদলে [] ফেরানো খুব গুরুত্বপূর্ণ: ফ্রন্টএন্ডে data.items.map(...) null-এর কারণে ক্র্যাশ করবে না।

৭. বিবর্তন কৌশল

পরিবর্তন সামঞ্জস্য ভাঙে কীভাবে সামলাবেন
ঐচ্ছিক ফিল্ড যোগ না সোজা প্রকাশ করুন
বাধ্যতামূলক ফিল্ড যোগ হ্যাঁ ডিফল্ট মান দিন, বা সংস্করণ দিয়ে প্রকাশ
ফিল্ড সরানো হ্যাঁ আগে ডেপ্রিকেটেড চিহ্নিত করুন, কলার দেখুন, তারপর সরান
ফিল্ড টাইপ বদলানো হ্যাঁ নতুন ফিল্ড-নাম যোগ করুন ও রূপান্তরকালে দুইটিতে লিখুন
এনাম মান-সেট বদলানো নির্ভর করে অজানা মানের জন্য ক্লায়েন্টে ফলব্যাক থাকতে হবে
ফিল্ড সেমান্টিক্স বদলানো হ্যাঁ এটিকে নতুন ফিল্ড ধরুন

শৃঙ্খলার তিন নিয়ম:

  1. ক্লায়েন্ট অজানা ফিল্ড সহনশীল হতে হবে (additionalProperties: false দিয়ে নিজেকে ঘিরে ফেলবেন না);
  2. অজানা এনাম মানের জন্য ক্লায়েন্টে ফলব্যাক শাখা থাকতে হবে;
  3. ডেপ্রিকেটেড ফিল্ড অন্তত একটি বড় সংস্করণ ধরে রাখুন, আর রেসপন্স হেডার বা ডকুমেন্টেশনে Deprecation দিয়ে চিহ্নিত করুন।

৮. একটি পূর্ণ উদাহরণ

{
  "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।

৯. চেকলিস্ট

  • [ ] টপ লেভেল অ্যারে নয়; সমানভাবে data এনভেলপ ব্যবহার করুন;
  • [ ] এরর রেসপন্স RFC 9457 অনুসরণ করে ও স্থিতিশীল type রাখে;
  • [ ] পেজিনেশনের আকৃতি ব্যবসায়িক পরিস্থিতির সঙ্গে মেলে;
  • [ ] ID স্ট্রিং, আর সময় RFC 3339;
  • [ ] null ও অনুপস্থিতের সেমান্টিক্স ডকুমেন্টেশনে লেখা;
  • [ ] নামকরণ শৈলী প্রোজেক্টজুড়ে সমান, এনাম ছোট হাতের স্ট্রিং;
  • [ ] অজানা ফিল্ড ও অজানা এনাম মানের জন্য ক্লায়েন্টে ফলব্যাক আছে;
  • [ ] ভাঙনকারী পরিবর্তন সংস্করণ বা দুই-লেখা রূপান্তরের মধ্য দিয়ে যায়।

সাধারণ প্রশ্ন

কীভাবে একটি বিবর্তনযোগ্য JSON API নকশা করব?

লিস্ট ও ডিটেইল সমানভাবে data এনভেলপে মুড়ুন আর টপ লেভেল কখনো অ্যারে করবেন না, যাতে চুক্তি না ভেঙে পেজিনেশন ও ট্রেসিং ID যোগ করা যায়। স্ট্রিং ID ও RFC 3339 টাইমস্ট্যাম্প ব্যবহার করুন, আর null বনাম অনুপস্থিতের সেমান্টিক্স ডকুমেন্টেশনে লিখুন। নতুন ফিল্ড ঐচ্ছিক ও সেমান্টিকভাবে স্বাধীন রাখুন, আর অজানা ফিল্ড ও এনাম মানের জন্য ক্লায়েন্টে ফলব্যাক দিন।

নিজের এরর ফরম্যাট উদ্ভাবন করা উচিত নয় কেন?

নিজের এরর কাঠামো উদ্ভাবন করা সহযোগিতার খরচের বড় উৎস। RFC 9457-এর application/problem+json কপি করুন, স্থিতিশীল type দিয়ে সমস্যা চিহ্নিত করুন, আর balance ও required-এর মতো এক্সটেনশন ফিল্ড অনুমোদন করুন, যাতে ফ্রন্টএন্ড detail টেক্সট পার্স না করে নির্দিষ্ট বার্তা বানাতে পারে।

প্রতিটি এন্ডপয়েন্টে cursor পেজিনেশন ব্যবহার করা উচিত?

না। অ্যাডমিন টেবিলে পেজ-লাফ ও মোট দরকার, তাই offset পেজিনেশন ভালো মানায়; ব্যবহারকারীর infinite ফিডে cursor পেজিনেশন ব্যবহার করুন, যা গভীর পেজে স্থিতিশীল থাকে আর ডেটা বদলালে ডুপ্লিকেট বা ফাঁক এড়ায়। পরিস্থিতির পেজ-লাফ ও মোট দরকার কি না দেখে বাছুন।