পাঠ ০৬ · ৫১-এর মধ্যে · মডিউল ২
Home / Courses / System Design / HTTP/HTTPS ও REST API ডিজাইন

HTTP/HTTPS ও REST API ডিজাইন

HTTP/HTTPS & REST API design
৯ মিনিট পড়া মধ্যম · Intermediate Python কোডসহ সম্পূর্ণ বাংলায়

এই পাঠে যা শিখবেন

  • HTTP মেথড এবং গুরুত্বপূর্ণ স্ট্যাটাস কোডের অর্থ ও ব্যবহার
  • HTTPS/TLS সংক্ষেপে — কেন এনক্রিপশন প্রয়োজন (L40-এ বিস্তারিত)
  • REST-এর মূল নীতি — রিসোর্স-ভিত্তিক URL, স্টেটলেসনেস, CRUD ম্যাপিং, আইডেম্পোটেন্সি
  • একটি "posts" রিসোর্সের সম্পূর্ণ REST এন্ডপয়েন্ট ডিজাইন
  • Python দিয়ে একটি ইন-মেমরি মক REST API ইমপ্লিমেন্ট করে CRUD অপারেশন দেখা

১ · HTTP মেথড ও স্ট্যাটাস কোড

HTTPHyperText Transfer Protocolওয়েবের যোগাযোগ প্রোটোকল — ক্লায়েন্ট একটি "মেথড" (GET, POST...) দিয়ে রিকোয়েস্ট পাঠায়, সার্ভার একটি "স্ট্যাটাস কোড"-সহ রেসপন্স পাঠায়। হলো ওয়েবের যোগাযোগ প্রোটোকল। প্রতিটি রিকোয়েস্টের একটি মেথড থাকে, যা বলে দেয় ক্লায়েন্ট কী করতে চাইছে —

GET
ডেটা পড়া — কোনো সাইড-ইফেক্ট নেই
POST
নতুন রিসোর্স তৈরি করা
PUT
একটি রিসোর্স সম্পূর্ণভাবে প্রতিস্থাপন/আপডেট করা
PATCH
একটি রিসোর্সের আংশিক আপডেট
DELETE
একটি রিসোর্স মুছে ফেলা

সার্ভার প্রতিটি রিকোয়েস্টের সাথে একটি স্ট্যাটাস কোড রিটার্ন করে, যা রেসপন্সের ধরন বলে দেয় —

200 OK
সফল
201 Created
নতুন রিসোর্স সফলভাবে তৈরি হয়েছে
204 No Content
সফল, কিন্তু রেসপন্স বডিতে কিছু নেই (যেমন সফল DELETE)
301 Moved Permanently
রিসোর্স স্থায়ীভাবে অন্য URL-এ সরানো হয়েছে
400 Bad Request
ক্লায়েন্টের রিকোয়েস্ট ভুল/অসম্পূর্ণ
401 Unauthorized
অথেন্টিকেশন লাগবে (L39)
403 Forbidden
অথেন্টিকেটেড কিন্তু অনুমতি নেই
404 Not Found
রিসোর্স পাওয়া যায়নি
429 Too Many Requests
রেট লিমিট (L35) অতিক্রম করেছে
500 Internal Server Error
সার্ভারে অপ্রত্যাশিত সমস্যা
503 Service Unavailable
সার্ভার সাময়িকভাবে ওভারলোডেড/ডাউন

২ · HTTPS = HTTP + TLS

সাধারণ HTTP ডেটা প্লেইন টেক্সটে পাঠায় — মাঝপথে কেউ (ISP, পাবলিক Wi-Fi-এর অন্য কেউ) সেটা পড়ে ফেলতে পারে। HTTPS এই যোগাযোগ TLS দিয়ে এনক্রিপ্ট করে — প্রাথমিক হ্যান্ডশেকে অ্যাসিমেট্রিক ক্রিপ্টোগ্রাফি (RSA/ECC — discrete-math কোর্সের RSA পাঠের সাথে সরাসরি সম্পর্কিত) ব্যবহার করে একটি শেয়ারড কী নিরাপদে বিনিময় করে, তারপর বাকি যোগাযোগ দ্রুততর সিমেট্রিক এনক্রিপশন দিয়ে হয়। L40-এ আমরা এনক্রিপশনের এই পুরো প্রক্রিয়া ও কেন এভাবে ডিজাইন করা হয়েছে তা বিস্তারিত দেখব।

৩ · REST — রিসোর্স-ভিত্তিক API ডিজাইন

REST একটি আর্কিটেকচারাল স্টাইল, প্রোটোকল নয়। এর মূল নীতিগুলো —

  • রিসোর্স-ভিত্তিক URL — URL একটি "noun" (বিশেষ্য) বোঝায়, যেমন /posts, ক্রিয়া (verb) নয় (/getPosts নয়)। কোন অপারেশন হবে তা ঠিক করে HTTP মেথড।
  • স্টেটলেসনেস — প্রতিটি রিকোয়েস্টে সার্ভারের প্রয়োজনীয় সব তথ্য থাকে, সার্ভার আগের রিকোয়েস্ট মনে রাখে না (L12-এ stateless সার্ভিস ডিজাইনের সাথে সরাসরি সম্পর্কিত)।
  • স্ট্যান্ডার্ড মেথড CRUD-এ ম্যাপ হয় — GET=Read, POST=Create, PUT/PATCH=Update, DELETE=Delete।
  • আইডেম্পোটেন্সি — GET, PUT, DELETE একই রিকোয়েস্ট বারবার পাঠালে একই ফলাফল দেয় (একই রিসোর্সকে দুবার ডিলিট করলেও চূড়ান্ত অবস্থা একই — "নেই")। কিন্তু POST বারবার পাঠালে প্রতিবার একটি নতুন রিসোর্স তৈরি হতে পারে — নন-আইডেম্পোটেন্ট। এই পার্থক্য গুরুত্বপূর্ণ কারণ নেটওয়ার্ক ব্যর্থতার পর নিরাপদে রিট্রাই (L27) করা যায় শুধু আইডেম্পোটেন্ট অপারেশনে।

৪ · ওয়ার্কড উদাহরণ — "posts" রিসোর্সের REST ডিজাইন

GET /posts
সব পোস্টের তালিকা
GET /posts/{id}
একটি নির্দিষ্ট পোস্ট
POST /posts
নতুন পোস্ট তৈরি (201 রিটার্ন)
PUT /posts/{id}
নির্দিষ্ট পোস্ট আপডেট
DELETE /posts/{id}
নির্দিষ্ট পোস্ট মুছে ফেলা (204 রিটার্ন)
ক্লায়েন্ট Client POST /posts অ্যাপ সার্ভার App Server ডেটাবেস Database 201 Created
একটি সাধারণ REST রিকোয়েস্ট — ক্লায়েন্ট POST /posts পাঠায়, সার্ভার ডেটাবেসে সেভ করে 201 Created রিটার্ন করে।

৫ · কোডে দেখা — একটি মক REST API

নিচে একটি ইন-মেমরি dict-ভিত্তিক "posts" স্টোর দিয়ে একটি ছোট মক REST API — প্রতিটি ফাংশন একটি HTTP মেথডের সমতুল্য এবং একটি বাস্তবসম্মত স্ট্যাটাস কোড রিটার্ন করে।

Python
posts_db = {}
next_id = 1

def get_all():
    return {"status": 200, "data": list(posts_db.values())}

def get_one(post_id):
    if post_id not in posts_db:
        return {"status": 404, "data": None}
    return {"status": 200, "data": posts_db[post_id]}

def create(title, body):
    global next_id
    post = {"id": next_id, "title": title, "body": body}
    posts_db[next_id] = post
    next_id += 1
    return {"status": 201, "data": post}

def update(post_id, title, body):
    if post_id not in posts_db:
        return {"status": 404, "data": None}
    posts_db[post_id] = {"id": post_id, "title": title, "body": body}
    return {"status": 200, "data": posts_db[post_id]}

def delete(post_id):
    if post_id not in posts_db:
        return {"status": 404, "data": None}
    del posts_db[post_id]
    return {"status": 204, "data": None}

# --- ডেমো: প্রতিটি অপারেশন ---
print("POST /posts:", create("প্রথম পোস্ট", "হ্যালো ওয়ার্ল্ড"))
print("POST /posts:", create("দ্বিতীয় পোস্ট", "আরেকটি পোস্ট"))
print("GET /posts:", get_all())
print("GET /posts/1:", get_one(1))
print("PUT /posts/1:", update(1, "প্রথম পোস্ট (আপডেটেড)", "নতুন কনটেন্ট"))
print("DELETE /posts/2:", delete(2))
print("GET /posts/2 (মুছে ফেলার পর):", get_one(2))

    
লক্ষ্য করুন — create() কল করলে প্রতিবার একটি নতুন ID-সহ পোস্ট তৈরি হয় (POST নন-আইডেম্পোটেন্ট), কিন্তু update() একই আর্গুমেন্ট দিয়ে বারবার কল করলেও চূড়ান্ত অবস্থা একই থাকে (PUT আইডেম্পোটেন্ট)। একইভাবে delete() দ্বিতীয়বার কল করলে 404 রিটার্ন করবে (রিসোর্স আগেই নেই), কিন্তু "সিস্টেমের চূড়ান্ত অবস্থা" (রিসোর্সটি নেই) একই থাকে — এটাই আইডেম্পোটেন্সির প্রকৃত সংজ্ঞা।
মূল কথা · Key takeaway

HTTP মেথড ও স্ট্যাটাস কোড একটি সার্বজনীন ভাষা তৈরি করে যা যেকোনো ক্লায়েন্ট (ব্রাউজার, মোবাইল অ্যাপ, অন্য সার্ভিস) বোঝে। REST-এর রিসোর্স-ভিত্তিক, স্টেটলেস ডিজাইন সরাসরি সাহায্য করে হরাইজন্টাল স্কেলিং (M3) এবং লোড ব্যালেন্সিংকে (L11) — যেকোনো সার্ভার ইনস্ট্যান্স যেকোনো রিকোয়েস্ট হ্যান্ডল করতে পারে। L07-এ আমরা দেখব gRPC ও GraphQL কীভাবে REST-এর কিছু সীমাবদ্ধতা (over/under-fetching) সমাধান করে।

ভাবনার প্রশ্ন

প্রতিটি প্রশ্ন নিজে কিছুক্ষণ ভাবুন — তারপর "→ উত্তর" চাপুন।

প্র ০১ POST কেন নন-আইডেম্পোটেন্ট, এবং এটি বাস্তবে (যেমন একটি পেমেন্ট রিকোয়েস্টে) কী সমস্যা তৈরি করতে পারে?

POST সাধারণত নতুন রিসোর্স তৈরি করে — একই রিকোয়েস্ট দুবার পাঠালে দুটি ভিন্ন রিসোর্স (দুটি ভিন্ন ID) তৈরি হয়ে যায়। একটি পেমেন্ট API-তে যদি ক্লায়েন্ট একটি "চার্জ কর" POST পাঠায় এবং নেটওয়ার্ক টাইমআউটের কারণে রেসপন্স না পায়, ক্লায়েন্ট বুঝতে পারে না রিকোয়েস্টটি আসলে প্রসেস হয়েছিল কি না। যদি সে নিরাপদ ভেবে আবার POST পাঠায়, গ্রাহক দুবার চার্জ হয়ে যেতে পারে। এই সমস্যার সমাধান L38-এ — idempotency key ব্যবহার করে, যা POST-কেও কার্যকরভাবে আইডেম্পোটেন্ট বানিয়ে দেয়।

প্র ০২ REST-এর "স্টেটলেসনেস" নীতি কীভাবে সরাসরি সিস্টেমকে স্কেল করতে সাহায্য করে?

স্টেটলেস মানে প্রতিটি রিকোয়েস্টে সার্ভারের প্রয়োজনীয় সব তথ্য (অথেন্টিকেশন টোকেন, প্রয়োজনীয় ডেটা) থাকে — সার্ভার কোনো "আগের রিকোয়েস্ট মনে রাখা" স্টেট বহন করে না। ফলে লোড ব্যালেন্সার (L11) যেকোনো রিকোয়েস্ট যেকোনো সার্ভার ইনস্ট্যান্সে পাঠাতে পারে — কোনো নির্দিষ্ট সার্ভারে "স্টিকি" থাকার দরকার নেই। এতে হরাইজন্টাল স্কেলিং (নতুন সার্ভার যোগ করা) অনেক সহজ হয়ে যায়, কারণ প্রতিটি সার্ভার ইনস্ট্যান্স সম্পূর্ণ পরিবর্তনযোগ্য (interchangeable) — L12-এ stateless বনাম stateful ডিজাইনে এটি আরও গভীরে দেখব।

প্র ০৩ একটি API যদি DELETE-এর জন্য 200 status ব্যবহার না করে 204 ব্যবহার করে, এর পেছনে যুক্তি কী?

200 OK সাধারণত বোঝায় রেসপন্সে অর্থবহ ডেটা (বডি) আছে। কিন্তু একটি সফল DELETE-এর পর রিটার্ন করার মতো কোনো "রিসোর্স" থাকে না — রিসোর্সটি তো মুছেই ফেলা হয়েছে। 204 No Content স্পষ্টভাবে বলে দেয় "অপারেশন সফল হয়েছে, কিন্তু রেসপন্স বডিতে কিছু নেই" — যা ক্লায়েন্টকে বিভ্রান্ত না করে সঠিক প্রত্যাশা তৈরি করে এবং API-এর সিমান্টিক স্পষ্টতা বাড়ায়।

অনুশীলন

  1. ডিজাইন করুন: একটি "comments" রিসোর্সের জন্য (প্রতিটি কমেন্ট একটি নির্দিষ্ট পোস্টের অধীনে থাকে) সম্পূর্ণ REST এন্ডপয়েন্ট তালিকা লিখুন — তালিকা দেখা, একটি নির্দিষ্ট কমেন্ট দেখা, তৈরি করা, আপডেট করা, ও মুছে ফেলা।

    GET /posts/{post_id}/comments (নির্দিষ্ট পোস্টের সব কমেন্ট), GET /posts/{post_id}/comments/{comment_id} (একটি নির্দিষ্ট কমেন্ট), POST /posts/{post_id}/comments (নতুন কমেন্ট তৈরি, 201 রিটার্ন), PUT /posts/{post_id}/comments/{comment_id} (কমেন্ট আপডেট), DELETE /posts/{post_id}/comments/{comment_id} (কমেন্ট মুছে ফেলা, 204 রিটার্ন)। লক্ষ্য করুন URL-এর নেস্টিং (/posts/{id}/comments) স্পষ্টভাবে বোঝায় যে একটি কমেন্ট একটি নির্দিষ্ট পোস্টের অধীনস্থ রিসোর্স।

  2. কোড লিখুন: উপরের মক API-তে একটি list_by_title_contains(keyword) ফাংশন যোগ করুন যা posts_db-এর মধ্যে টাইটেলে নির্দিষ্ট কীওয়ার্ড থাকা সব পোস্ট রিটার্ন করে (status 200 সহ)। GET /posts?search=... এর মতো একটি কোয়েরি-প্যারামিটার-ভিত্তিক ফিল্টারিং এন্ডপয়েন্টের সমতুল্য।

    def list_by_title_contains(keyword): matches = [p for p in posts_db.values() if keyword in p["title"]]; return {"status": 200, "data": matches} — এটি REST-এ কোয়েরি প্যারামিটার (GET /posts?search=keyword) দিয়ে ফিল্টারিং করার ধারণাকে প্রতিফলিত করে, যেখানে URL পাথ রিসোর্স নির্দেশ করে (/posts) কিন্তু কোয়েরি প্যারামিটার ফলাফল সংকীর্ণ করে, নতুন কোনো রিসোর্স তৈরি না করেই।

আরও পড়ুন · ABCL TECH-এ আপনার পরবর্তী পদক্ষেপ

আগের পাঠ
ক্লায়েন্ট-সার্ভার মডেল ও DNS