Building Blocks১১ মিনিট পাঠ

API ডিজাইন (API Design)

আপনার সিস্টেম এবং বাইরের জগতের মধ্যকার চুক্তি
scope:বিল্ডিং ব্লকdifficulty:বিগিনার-মাঝারি

API কী?

একটি API (Application Programming Interface) হলো খাবারের হোটেলের মেনুর মতো। আপনি রান্নাঘরে ঢুকে নিজের খাবার নিজে তৈরি করবেন না। আপনি শুধু মেনু দেখবেন, কী খাবেন তা বেছে নেবেন, এবং রান্নাঘর আপনার জন্য সেটি তৈরি করবে। এই মেনুটিই হলো ইন্টারফেস — এটি আপনাকে জানায় কী কী খাবার পাওয়া যাচ্ছে, আপনাকে কী দিতে হবে ("আপনার কাবাব বা গ্রিল কেমন হবে?"), এবং আপনি বিনিময়ে কী পাবেন।

সফটওয়্যারের ভাষায়, একটি API নির্ধারণ করে কীভাবে দুটি সিস্টেম নিজেদের মধ্যে কথা বলে। আপনার মোবাইল অ্যাপ একটি API-এর মাধ্যমে আপনার ব্যাকএন্ডের সাথে কথা বলে। আপনার ব্যাকএন্ড স্ট্রাইপ (Stripe)-এর পেমেন্ট সিস্টেমের সাথে API-এর মাধ্যমে কথা বলে। API সব জায়গাতেই আছে।

একটি ভালো API ডিজাইন অনেক গুরুত্বপূর্ণ কারণ:

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

REST: সবচেয়ে কমন API স্টাইল

REST (REpresentational State Transfer) হলো ওয়েবের সবচেয়ে জনপ্রিয় API স্টাইল। এটি রিসোর্সগুলোর উপর বিভিন্ন অপারেশন সম্পাদনের জন্য মানসম্মত এবং সাধারণ HTTP মেথড ব্যবহার করে।

এর মূল নীতিগুলো হলো:

  • রিসোর্সসমূহ: URL-এ থাকা সবকিছুই হলো এক একটি রিসোর্স। ইউজারগুলো /users-এ থাকে, কোনো নির্দিষ্ট ইউজার পেতে /users/42, তার লেখা পোস্টগুলো পেতে /users/42/posts
  • HTTP মেথডসমূহ: সঠিক কাজের জন্য সঠিক ক্রিয়াপদ বা ভার্ব (verb) ব্যবহার করুন:

GET /users/42 — ৪২ নাম্বার ইউজারকে রিড বা পড়তে
POST /users — নতুন ইউজার তৈরি করতে
PUT /users/42 — ৪২ নাম্বার ইউজারকে পুরোপুরি রিপ্লেস করতে
PATCH /users/42 — ৪২ নাম্বার ইউজারের নির্দিষ্ট কিছু ফিল্ড আপডেট করতে
DELETE /users/42 — ৪২ নাম্বার ইউজারকে মুছে ফেলতে

  • স্টেটলেস (Stateless): প্রতিটি রিকোয়েস্টে প্রয়োজনীয় সমস্ত তথ্য থাকতে হবে। কারণ সার্ভার কখনোই আপনার আগের রিকোয়েস্ট মনে রাখে না।
  • JSON: অধিকাংশ REST API-ই রিকোয়েস্ট/রেসপন্স বডির জন্য JSON ব্যবহার করে।
REST ভার্বস — GET, POST, PUT, DELETE

HTTP স্ট্যাটাস কোড (HTTP Status Codes)

স্ট্যাটাস কোড ক্লায়েন্টকে বলে যে কী ঘটেছে। এগুলোকে সার্ভারের মুখের অভিব্যক্তি বা ফেসিয়াল এক্সপ্রেশনের (facial expressions) সাথে তুলনা করতে পারেন:

2xx — সাফল্য বা Success (হাসি মুখ)

  • 200 OK — সবকিছু ঠিক আছে। এই যে আপনার ডেটা।
  • 201 Created — নতুন রিসোর্স সফলভাবে তৈরি করা হয়েছে।
  • 204 No Content — সফল হয়েছে, কিন্তু রিটার্ন করার মতো কিছু নেই (DELETE-এর ক্ষেত্রে এটি সাধারণত ব্যবহার করা হয়)।

3xx — রিডিরেকশন বা Redirection (অন্য কোনো দিকে ইশারা করা)

  • 301 Moved Permanently — এই রিসোর্সটির জন্য একটি নতুন URL সেট করা হয়েছে, এবং এটি চিরস্থায়ী।
  • 304 Not Modified — আপনার ক্যাশ করা সংস্করণটি ব্যবহার করুন, কিছুই পরিবর্তন হয়নি।

4xx — ক্লায়েন্ট এরর বা Client Error (আপনি গণ্ডগোল করেছেন)

  • 400 Bad Request — আপনার রিকোয়েস্টের কোনো মানে হয় না।
  • 401 Unauthorized — আপনি কে? (পরিচয় যাচাই বা প্রমাণীকরণ করা হয়নি।)
  • 403 Forbidden — আমি জানি আপনি কে, কিন্তু আপনার এই কাজ করার অনুমতি নেই।
  • 404 Not Found — এই রিসোর্সের কোনো অস্তিত্ব নেই।
  • 429 Too Many Requests — একটু আস্তে! আপনি রেট লিমিট অতিক্রম করে ফেলছেন।

5xx — সার্ভার এরর বা Server Error (আমরা গণ্ডগোল করেছি)

  • 500 Internal Server Error — আমাদের প্রান্তে কোনো কিছু ভেঙে পড়েছে বা নষ্ট হয়ে গেছে।
  • 502 Bad Gateway — আমাদের পেছনের সার্ভারটি ত্রুটিপূর্ণ।
  • 503 Service Unavailable — আমরা ওভারলোডেড অথবা মেনটেইনান্স (maintenance) এর কাজে ব্যস্ত আছি।
এক নজরে HTTP স্ট্যাটাস কোডগুলো

RESTful API ডিজাইনের একটি উদাহরণ

from flask import Flask, jsonify, request
app = Flask(__name__)
# In-memory "database"
users = {
1: {"id": 1, "name": "Anika", "email": "[email protected]"},
2: {"id": 2, "name": "Rafi", "email": "[email protected]"},
}
next_id = 3
# GET /users — List all users (with pagination)
@app.route("/api/v1/users", methods=["GET"])
def list_users():
page = request.args.get("page", 1, type=int)
per_page = request.args.get("per_page", 10, type=int)
all_users = list(users.values())
start = (page - 1) * per_page
end = start + per_page
return jsonify({
"data": all_users[start:end],
"page": page,
"per_page": per_page,
"total": len(all_users)
}), 200
# GET /users/:id — Get a specific user
@app.route("/api/v1/users/<int:user_id>", methods=["GET"])
def get_user(user_id):
user = users.get(user_id)
if not user:
return jsonify({"error": "User not found"}), 404
return jsonify({"data": user}), 200
# POST /users — Create a new user
@app.route("/api/v1/users", methods=["POST"])
def create_user():
global next_id
body = request.get_json()
if not body or "name" not in body or "email" not in body:
return jsonify({"error": "name and email are required"}), 400
user = {"id": next_id, "name": body["name"], "email": body["email"]}
users[next_id] = user
next_id += 1
return jsonify({"data": user}), 201
# DELETE /users/:id — Delete a user
@app.route("/api/v1/users/<int:user_id>", methods=["DELETE"])
def delete_user(user_id):
if user_id not in users:
return jsonify({"error": "User not found"}), 404
del users[user_id]
return "", 204
Output
# GET /api/v1/users?page=1&per_page=10
# → 200 {"data": [...], "page": 1, "total": 2}

# POST /api/v1/users  {"name": "Sadia", "email": "[email protected]"}
# → 201 {"data": {"id": 3, "name": "Sadia", ...}}

# GET /api/v1/users/999
# → 404 {"error": "User not found"}
Click chart to zoom
REST রিকোয়েস্টের জীবনচক্র — ক্লায়েন্ট থেকে লোড ব্যালান্সার হয়ে API সার্ভারে একটি GET রিকোয়েস্ট যায়, যা ডাটাবেসে কুয়েরি (query) চালায় এবং একটি JSON ফলাফল ফেরত দেয়

API ভার্সন (API Versioning)

API গুলো ধীরে ধীরে বিকশিত হয়। আপনি নতুন ফিচার যোগ করবেন, ডেটা ফরম্যাট পরিবর্তন করবেন, কোনো কোনো এন্ডপয়েন্ট বা রুট মুছে ফেলবেন। কিন্তু আপনি সেইসব ক্লায়েন্ট বা ব্যবহারকারীদের কোড ভেঙে ফেলতে পারবেন না যারা পুরোনো আচরণের ওপর নির্ভরশীল। ভার্সন (Versioning) দিয়ে এই সমস্যার সমাধান করা হয়।

কমন বা সাধারণ কিছু কৌশল:

  • URL পাথ ভার্সনিং: /api/v1/users, /api/v2/users। এটি সবচেয়ে সাধারণ এবং সবচেয়ে বেশি দৃশ্যমান পদ্ধতি।
  • হেডার ভার্সনিং: Accept: application/vnd.myapi.v2+json। এতে URL গুলো দেখতে পরিষ্কার লাগে কিন্তু ব্রাউজারে পরীক্ষা করা কঠিন হয়ে যায়।
  • কুয়েরি প্যারামিটার: /api/users?version=2। ইমপ্লিমেন্ট বা প্রয়োগ করা সহজ, তবে এটি বেশ বিশৃঙ্খল একটা পরিবেশ তৈরি করতে পারে।

বাস্তবে URL পাথ ভার্সনিং-ই সবচেয়ে বেশি ব্যবহার করা হয় কারণ এটি স্পষ্ট এবং লোড ব্যালান্সার স্তরে ক্যাশ বা রাউট করাও বেশ সহজ।

প্যাগিনেশন (Pagination)

আপনি কখনোও চাইবেন না যে একটি রিকোয়েস্ট ১০০ মিলিয়ন রেকর্ড ফেরত দিক। প্যাগিনেশন ডেটাকে ম্যানেজ করার উপযোগী ছোট ছোট খণ্ডে বিভক্ত করে রিটার্ন করে।

অফসেট-ভিত্তিক (Offset-based): GET /posts?page=3&per_page=20। এটি সহজ হলেও এতে কিছু সমস্যা রয়েছে: যদি দুটি পেজ রিকোয়েস্টের মধ্যে নতুন পোস্ট যোগ করা হয়, তবে আপনি ডুপিকেট (duplicate) আইটেম দেখতে পারেন বা কোনো আইটেম মিস করে যেতে পারেন। বড় অফসেটের (offset) কারণে এটি ধীরগতির হতে পারে (ডাটাবেসকে হাজার হাজার সারি বা রোউ এড়িয়ে যেতে হবে)।

কার্সর-ভিত্তিক (Cursor-based): GET /posts?cursor=abc123&limit=20। কার্সরটি হলো একটি টোকেন (সাধারণত একটি এনকোড করা টাইমস্ট্যাম্প বা আইডি) যা নির্দেশ করে শেষ পৃষ্ঠাটি কোথায় গিয়ে থামে। বড় ডেটাসেটের জন্য এটি অনেক বেশি কার্যকর এবং এটিতে কোনো ডুপ্লিকেট বা স্কিপ করার সমস্যা নেই। প্রায় সব আধুনিক API (যেমন Twitter, Facebook) এই পদ্ধতি ব্যবহার করে থাকে।

প্যাগিনেশনের সাথে কার্সরের ব্যবহার

রেট লিমিটিং (Rate Limiting)

রেট লিমিটিং ছাড়া একজন ক্লায়েন্ট লক্ষ লক্ষ রিকোয়েস্ট পাঠিয়ে আপনার API-কে মুহূর্তেই ওভারলোড করে ফেলতে পারে। রেট লিমিটিং কোনো নির্দিষ্ট সময়সীমার মধ্যে ক্লায়েন্টের করা রিকোয়েস্টের সংখ্য নির্দিষ্ট করে বা বাধা তৈরি করে।

রেট লিমিট হেডারগুলো ক্লায়েন্টদের তাদের বর্তমান স্ট্যাটাস সম্পর্কে ধারণা দেয়:

  • X-RateLimit-Limit: 100 — আপনি প্রতি উইন্ডোতে ১০০টি রিকোয়েস্ট করতে পারবেন
  • X-RateLimit-Remaining: 42 — আপনার হাতে আর ৪২টি রিকোয়েস্ট বাকি আছে
  • X-RateLimit-Reset: 1640000000 — ইউনিস্ক টাইমস্ট্যাম্প অনুযায়ী এই সময়ে উইন্ডোটি পুনরায় সেট করা হবে

লিমিট শেষ হয়ে গেলে 429 Too Many Requests রিটার্ন করুন। আমরা রেট লিমিটার ডিজাইন লেসনে এ বিষয়ে আরও বিস্তারিত আলোচনা করব।

API গেটওয়ে — সিঙ্গেল এন্ট্রি পয়েন্ট

REST ছাড়িয়ে: GraphQL এবং gRPC

GraphQL (ফেসবুক কর্তৃক ডেভেলপ করা) ক্লায়েন্টদের ঠিক যতটুকু ডেটা প্রয়োজন, কেবল ততটুকুই রিকোয়েস্ট করার অধিকার দেয়। একাধিক REST এন্ডপয়েন্ট বা রুটের পরিবর্তে, এখানে কেবল একটি এন্ডপয়েন্ট থাকে যেখানে আপনি আপনার কুয়েরি পাঠান:

query { user(id: 42) { name, email, posts { title } } }

এটি ওভার-ফেচিং (over-fetching) (REST সব ফিল্ড রিটার্ন করে যেখানে আপনার আসলে মাত্র দুটি ফিল্ড প্রয়োজন) এবং আন্ডার-ফেচিং (under-fetching) (সম্পর্কযুক্ত ডেটা পাওয়ার জন্য একাধিক REST কল করতে হয়) এর সমস্যা সমাধান করে। মোবাইল অ্যাপগুলোর জন্য এটি দারুণ কাজ করে কারণ সেখানে ব্যান্ডউইথ এবং পারফর্ম্যান্স অনেক গুরুত্বপূর্ণ।

gRPC (গুগল কর্তৃক ডেভেলপ করা) JSON-এর পরিবর্তে প্রোটোকল বাফার (Protocol Buffers - বাইনারি ফরম্যাট) ব্যবহার করে। এটি REST-এর চেয়ে অনেক দ্রুত কারণ বাইনারি সাধারণ টেক্সটের চেয়ে ছোট এবং এটি HTTP/2 ফিচার যেমন স্ট্রিমিং এবং মাল্টিপ্লেক্সিং সমর্থন করে। এটি ইন্টারনাল সার্ভিস-টু-সার্ভিস কমিউনিকেশনের জন্য ব্যবহৃত হয় যেখানে হিউম্যান রিডঅ্যাবিলিটি (human readability) বা মানুষের পড়ার সুবিধার চেয়ে ডেটার পারফরম্যান্স বেশি গুরুত্বপূর্ণ।

কখন কোনটি ব্যবহার করবেন:

  • REST: পাবলিক API, সিম্পল CRUD অ্যাপ্লিকেশন, এবং যখন আপনি সর্বোচ্চ কম্প্যাটিবিলিটি চান।
  • GraphQL: জটিল ডেটার প্রয়োজনীয়তা, ভারী মোবাইল অ্যাপ, এবং যখন ক্লায়েন্টের ফ্লেক্সিবিলিটি বা নমনীয়তা প্রয়োজন।
  • gRPC: অভ্যন্তরীণ বা ইন্টারনাল মাইক্রোসার্ভিস কমিউনিকেশন, একদম লো-ল্যাটিনসি (low-latency) এবং দ্রুত ডেটা স্ট্রিমিং এর কাজের জন্য।
Click chart to zoom
GraphQL কুয়েরি রিজোলিউশন — একটি সিঙ্গেল বা একক রিকোয়েস্ট একাধিক ব্যাকএন্ড সার্ভিস থেকে ফিল্ডগুলো সংগ্রহ করে এবং ক্লায়েন্ট ঠিক যেভাবে ডেটা রিকোয়েস্ট করেছে ঠিক সেই শেপেই ডেটা ফিল্ডগুলো রিটার্ন করে
Note: সাক্ষাৎকারের টিপস: সিস্টেম ডিজাইনের সাক্ষাৎকারে আর্কিটেকচার নিয়ে গভীরে ঢোকার আগেই সব সময় API-গুলো ডিফাইন বা নির্ধারণ করুন। প্রতিটি এন্ডপয়েন্টের HTTP মেথড, রিকোয়েস্ট/রেসপন্স ফরম্যাট এবং স্ট্যাটাস কোডগুলো স্পষ্টভাবে উল্লেখ করুন। এটি আপনার পরিষ্কার চিন্তা-ভাবনার প্রমাণ দেয় এবং ডিজাইন করার জন্য একটি শক্তিশালী কন্টাক্ট বা ভিত্তি যোগায়। উদাহরণস্বরূপ: 'POST /api/v1/urls — একটি দীর্ঘ URL নেয় এবং 201 Created এর সাথে একটি সংক্ষিপ্ত URL ফেরত দেয়।'

Key Metrics

REST JSON সিরিয়ালাইজেশন
n = রেসপন্স সাইজ
~০.১-১ ms \(O(n)\)
gRPC Protobuf সিরিয়ালাইজেশন
বাইনারি, JSON-এর চেয়ে ৩-১০ গুণ ছোট
~০.০১-০.১ ms \(O(n)\)
GraphQL কুয়েরি পার্সিং
কুয়েরির গভীরতা বা জটিলতার ওপর নির্ভর করে
~০.৫-৫ ms \(O(n)\)
অফসেট প্যাগিনেশন (Offset pagination - page 1000)
ডাটাবেসকে ১০০০ পৃষ্ঠার রোগুলো এড়িয়ে যেতে হয়
ধীরগতির \(O(offset + limit)\)
কার্সর প্যাগিনেশন (Cursor pagination)
সরাসরি কার্সর পজিশন বা অবস্থানে চলে যায়
দ্রুত \(O(limit)\)

ছোট কুইজ

কোনো ইউজারের নাম পরিবর্তন না করে শুধু তার ইমেইল আপডেট বা পরিবর্তন করতে আপনার কোন HTTP মেথডটি ব্যবহার করা উচিত?

পড়া চালিয়ে যান