পাঠ ০৭ · ৫১-এর মধ্যে · মডিউল ২
Home / Courses / System Design / gRPC ও GraphQL

gRPC ও GraphQL — মডার্ন API প্যারাডাইম

gRPC & GraphQL — modern API paradigms
৮ মিনিট পড়া মধ্যম · Intermediate Python কোডসহ সম্পূর্ণ বাংলায়

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

  • REST-এর over-fetching ও under-fetching সমস্যা ঠিক কীভাবে ঘটে
  • GraphQL কীভাবে single-endpoint, client-specified-fields মডেল দিয়ে সমস্যাটি সমাধান করে
  • gRPC কী, এবং কেন এটি ইন্টারনাল মাইক্রোসার্ভিস কমিউনিকেশনে জনপ্রিয়
  • Python দিয়ে REST-এর ফুল-পেলোড বনাম GraphQL-এর ফিল্টার্ড-পেলোডের বাস্তব পার্থক্য দেখা

১ · REST-এর সমস্যা — Over-fetching ও Under-fetching

L06-এ আমরা REST API ডিজাইন শিখেছি — প্রতিটি রিসোর্সের একটি নির্দিষ্ট এন্ডপয়েন্ট থাকে (যেমন GET /users/42), এবং সার্ভার সেই এন্ডপয়েন্টের জন্য পূর্বনির্ধারিত একটি ডেটা স্ট্রাকচার ফেরত দেয়। সমস্যাটা এখানেই — Over-fetchingOver-fetchingক্লায়েন্ট একটি API কল করে যতটুকু ডেটা আসলে দরকার তার চেয়ে অনেক বেশি ফিল্ড ফেরত পায় — যেমন শুধু ইউজারের নাম দরকার হলেও পুরো প্রোফাইল অবজেক্ট চলে আসে। হলো যখন ক্লায়েন্ট (ধরুন একটি মোবাইল অ্যাপের হোমপেজ, যেখানে শুধু ইউজারের নাম দেখানো দরকার) একটি এন্ডপয়েন্ট কল করে, কিন্তু সার্ভার ইমেইল, ফোন নম্বর, ঠিকানা, জন্মতারিখ — সব ফিল্ডসহ পুরো ইউজার অবজেক্ট ফেরত দেয়। মোবাইল নেটওয়ার্কে এই অপ্রয়োজনীয় ডেটা ব্যান্ডউইথ ও ব্যাটারি নষ্ট করে।

উল্টো সমস্যাও আছে — under-fetching, যখন একটি স্ক্রিন দেখাতে একাধিক রিসোর্সের ডেটা লাগে (যেমন ইউজারের প্রোফাইল + তার সাম্প্রতিক পোস্ট + তার ফলোয়ার সংখ্যা), কিন্তু REST-এ প্রতিটির জন্য আলাদা এন্ডপয়েন্ট কল করতে হয় — ৩টি নেটওয়ার্ক রাউন্ড-ট্রিপ, প্রতিটির নিজস্ব লেটেন্সি।

Over-fetching
দরকারের চেয়ে বেশি ফিল্ড আসে — অপচয় হওয়া ব্যান্ডউইথ।
Under-fetching
একটি স্ক্রিনের জন্য একাধিক API কল লাগে — এক্সট্রা রাউন্ড-ট্রিপ।

২ · GraphQL — ক্লায়েন্ট বলে দেয় কী চাই

GraphQLGraphQLএকটি কোয়েরি ভাষা ও রানটাইম যেখানে একটি মাত্র এন্ডপয়েন্টে ক্লায়েন্ট নিজে বলে দেয় ঠিক কোন ফিল্ড এবং কোন সম্পর্কিত রিসোর্স দরকার — সার্ভার শুধু সেগুলোই ফেরত দেয়। REST-এর বদলে একটি একক এন্ডপয়েন্ট (সাধারণত POST /graphql) ব্যবহার করে। ক্লায়েন্ট একটি "কোয়েরি" পাঠায় যাতে লেখা থাকে ঠিক কোন ফিল্ডগুলো দরকার — সার্ভারের resolver ফাংশনগুলো সেই কোয়েরি অনুযায়ী শুধু চাওয়া ডেটা জোগাড় করে ফেরত দেয়। এর ফলে over-fetching প্রায় শূন্যে নেমে আসে, আর একাধিক সম্পর্কিত রিসোর্স এক কলেই আনা যায় (under-fetching সমাধান)।

ট্রেড-অফ

GraphQL নমনীয়তা দেয়, কিন্তু জটিলতাও বাড়ায় — প্রতিটি কোয়েরি ভিন্ন হতে পারে বলে HTTP-লেভেল ক্যাশিং (যা REST-এ URL-ভিত্তিক সহজ) কঠিন হয়ে যায়, এবং সার্ভার-সাইডে রিসোর্স-ইন্টেন্সিভ কোয়েরি সীমিত করার (query complexity limiting) বাড়তি কাজ লাগে।

৩ · gRPC — ইন্টারনাল সার্ভিসের জন্য দ্রুততম পথ

gRPCgRPCGoogle-এর তৈরি একটি রিমোট প্রসিডিউর কল (RPC) ফ্রেমওয়ার্ক — HTTP/2-এর উপর চলে এবং ডেটা সিরিয়ালাইজ করে Protocol Buffers নামের বাইনারি ফরম্যাটে, যা JSON টেক্সটের চেয়ে ছোট ও দ্রুত পার্স হয়। REST বা GraphQL — দুটোই সাধারণত JSON (টেক্সট) ব্যবহার করে, যা মানুষের পড়ার জন্য সুবিধাজনক কিন্তু তুলনামূলক ভারী। gRPC বাইনারি Protocol Buffers ব্যবহার করে — অনেক ছোট পেলোড, দ্রুত সিরিয়ালাইজেশন/ডিসিরিয়ালাইজেশন, এবং স্ট্রং টাইপড স্কিমা (একটি .proto ফাইলে ফাংশন সিগনেচার আগে থেকে সংজ্ঞায়িত)। এটি HTTP/2-এর উপর চলার কারণে স্ট্রিমিংও সাপোর্ট করে — ক্লায়েন্ট বা সার্ভার একবার কানেকশন খুলে ধারাবাহিকভাবে বার্তা আদান-প্রদান করতে পারে।

সমস্যা হলো — বাইনারি ফরম্যাট ব্রাউজার-নেটিভ নয় (ব্রাউজার সহজে JSON পড়তে পারে, raw protobuf নয়), তাই gRPC মূলত ইন্টারনাল মাইক্রোসার্ভিস-টু-মাইক্রোসার্ভিস কমিউনিকেশনে ব্যবহৃত হয় (M7-এ মাইক্রোসার্ভিস আর্কিটেকচার দেখব), যেখানে ব্রাউজার কম্প্যাটিবিলিটির দরকার নেই — শুধু গতি ও টাইপ-সেফটি দরকার।

REST
সহজ, ক্যাশেবল (URL-ভিত্তিক), কিন্তু over/under-fetching-এর ঝুঁকি।
GraphQL
নমনীয়, একক এন্ডপয়েন্ট, কিন্তু ক্যাশিং জটিল।
gRPC
দ্রুত বাইনারি ফরম্যাট, স্ট্রিমিং, কিন্তু ব্রাউজার-নেটিভ নয় — ইন্টারনাল ব্যবহারের জন্য সেরা।
ক্লায়েন্ট Client REST এন্ডপয়েন্ট /users/42 (ফুল অবজেক্ট) GraphQL এন্ডপয়েন্ট শুধু চাওয়া ফিল্ড gRPC কল বাইনারি, ইন্টারনাল সার্ভিস রেসপন্স: সব ফিল্ড id, name, email, phone, address... রেসপন্স: id, name মাত্র যা কোয়েরিতে চাওয়া হয়েছে রেসপন্স: protobuf বাইট ছোট, দ্রুত পার্স
একই "ইউজারের নাম দেখাও" চাহিদার জন্য তিনটি ভিন্ন API প্যারাডাইম কীভাবে সাড়া দেয়।

৪ · কোড দিয়ে দেখা — Over-fetching বনাম ফিল্ড-ফিল্টারিং

নিচে একটি সরল সিমুলেশন — rest_get_user() সবসময় ইউজারের সব ফিল্ড ফেরত দেয় (REST-এর সাধারণ আচরণ), আর graphql_get_user(fields) শুধু ক্লায়েন্টের চাওয়া ফিল্ডগুলো ফিল্টার করে ফেরত দেয়। পেলোডের আকার তুলনা করে দেখা যাক over-fetching বাস্তবে কতটা অপচয় ঘটায়।

Python
def rest_get_user(user_id):
    # REST: সবসময় ইউজারের সব ফিল্ড ফেরত দেয়, ক্লায়েন্ট যা-ই চাক না কেন
    return {
        "id": user_id,
        "name": "Rahim Uddin",
        "email": "rahim@example.com",
        "phone": "+8801712345678",
        "address": "Dhaka, Bangladesh",
        "date_of_birth": "1995-04-12",
        "profile_picture_url": "https://cdn.example.com/u/42.jpg",
        "bio": "সফটওয়্যার ইঞ্জিনিয়ার, সিস্টেম ডিজাইন পড়তে ভালোবাসি।",
        "followers_count": 1240,
        "following_count": 180,
        "created_at": "2020-01-15T10:00:00Z",
        "last_login": "2026-07-29T22:10:00Z",
    }

def graphql_get_user(user_id, fields):
    # GraphQL: শুধু ক্লায়েন্টের চাওয়া ফিল্ডগুলো ফিল্টার করে ফেরত দেয়
    full_record = rest_get_user(user_id)
    return {f: full_record[f] for f in fields if f in full_record}

# হোমপেজে শুধু নাম ও ফলোয়ার সংখ্যা দেখাতে হবে
requested_fields = ["id", "name", "followers_count"]

rest_payload = rest_get_user(42)
graphql_payload = graphql_get_user(42, requested_fields)

print("=== REST রেসপন্স (সব ফিল্ড, চাওয়া হয়নি এমন ফিল্ডসহ) ===")
print(rest_payload)
print(f"মোট ফিল্ড সংখ্যা: {len(rest_payload)}")

print("\n=== GraphQL রেসপন্স (শুধু চাওয়া ৩টি ফিল্ড) ===")
print(graphql_payload)
print(f"মোট ফিল্ড সংখ্যা: {len(graphql_payload)}")

rest_size = len(str(rest_payload))
graphql_size = len(str(graphql_payload))
print(f"\nআনুমানিক পেলোড আকার (characters) — REST: {rest_size}, GraphQL: {graphql_size}")
print(f"GraphQL পেলোড প্রায় {rest_size / graphql_size:.1f}x ছোট")

    
লক্ষ্য করুন — rest_get_user() ১২টি ফিল্ড ফেরত দেয় হোমপেজের জন্য যেখানে মাত্র ৩টি দরকার ছিল। বাস্তব প্রোডাকশন সিস্টেমে এই পার্থক্য শত শত ফিল্ড ও নেস্টেড অবজেক্টের ক্ষেত্রে অনেক বড় হয়ে দাঁড়ায় — বিশেষ করে মোবাইল অ্যাপে, যেখানে প্রতি বাইট ব্যান্ডউইথ ও ব্যাটারির জন্য গুরুত্বপূর্ণ।
মূল কথা · Key takeaway

REST, GraphQL, gRPC — তিনটিই বৈধ টুল, প্রতিটির নিজস্ব সঠিক জায়গা আছে। পাবলিক-ফেসিং API যেখানে সরলতা ও ক্যাশিং গুরুত্বপূর্ণ, সেখানে REST ভালো। জটিল, নেস্টেড ডেটা প্রয়োজনের ক্লায়েন্ট (যেমন একটি ড্যাশবোর্ড) থাকলে GraphQL ভালো। ইন্টারনাল মাইক্রোসার্ভিস কমিউনিকেশনে যেখানে গতি ও টাইপ-সেফটি সবচেয়ে গুরুত্বপূর্ণ, সেখানে gRPC সেরা পছন্দ।

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

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

প্র ০১ GraphQL over-fetching সমাধান করলেও, এটি কেন সবসময় REST-এর "উন্নত সংস্করণ" নয়?

কারণ GraphQL নতুন জটিলতা যোগ করে। REST-এ প্রতিটি এন্ডপয়েন্ট নির্দিষ্ট URL-এ থাকায় CDN ও ব্রাউজার সহজে HTTP-লেভেলে ক্যাশ করতে পারে। GraphQL-এ সব কোয়েরি একই এন্ডপয়েন্টে (POST /graphql) যায় এবং প্রতিটি কোয়েরি ভিন্ন হতে পারে, তাই সহজ URL-ভিত্তিক ক্যাশিং কাজ করে না — আলাদা ক্যাশিং লেয়ার বানাতে হয়। এছাড়া একটি দুষ্ট বা ভুলভাবে লেখা কোয়েরি অনেক নেস্টেড ডেটা টানতে গিয়ে সার্ভারকে ওভারলোড করতে পারে, তাই query-complexity সীমাবদ্ধতা বসাতে হয় — যা REST-এ দরকার হয় না।

প্র ০২ gRPC কেন সাধারণত সরাসরি ব্রাউজার থেকে ব্যবহার করা যায় না?

gRPC HTTP/2-এর নির্দিষ্ট ফ্রেমিং ও Protocol Buffers-এর বাইনারি এনকোডিং-এর উপর নির্ভর করে, যা ব্রাউজারের নেটিভ fetch/XMLHttpRequest API সরাসরি সাপোর্ট করে না (ব্রাউজার HTTP/2 স্ট্রিম নিয়ন্ত্রণ পুরোপুরি এক্সপোজ করে না)। এই কারণে ব্রাউজার থেকে gRPC ব্যবহার করতে gRPC-Web-এর মতো একটি প্রক্সি লেয়ার লাগে যা রিকোয়েস্ট রূপান্তর করে। এই বাড়তি জটিলতার কারণে gRPC মূলত সার্ভার-টু-সার্ভার (ইন্টারনাল) যোগাযোগেই বেশি দেখা যায়, ব্রাউজার-ফেসিং API-তে নয়।

প্র ০৩ একটি বড় প্রোডাকশন সিস্টেমে REST, GraphQL ও gRPC — তিনটিই একসাথে ব্যবহৃত হওয়া কি স্বাভাবিক?

হ্যাঁ, এবং বাস্তবে এটি খুব সাধারণ। যেমন — মোবাইল/ওয়েব ক্লায়েন্টের জন্য একটি GraphQL API গেটওয়ে (M7/L26-এ বিস্তারিত) থাকতে পারে, যা ভেতরে গিয়ে বিভিন্ন মাইক্রোসার্ভিসের সাথে দ্রুত gRPC কল করে ডেটা জোগাড় করে, আর তৃতীয় পক্ষের পার্টনারদের জন্য একটি সহজ, স্থিতিশীল REST API আলাদাভাবে এক্সপোজ করা হতে পারে। "একটি সিস্টেম, একটি API প্যারাডাইম" — এই ধারণাটি বাস্তবে প্রায়ই ভুল প্রমাণিত হয়।

অনুশীলন

  1. চিন্তা করুন: একটি ই-কমার্স অ্যাপের প্রোডাক্ট লিস্টিং পেজে (যেখানে শুধু নাম, দাম, একটি ছবি ও রেটিং দরকার) REST নাকি GraphQL ব্যবহার করবেন এবং কেন?

    দুটোই কাজ করবে, তবে এই নির্দিষ্ট কেসে যদি প্রোডাক্ট অবজেক্টে অনেক ফিল্ড থাকে (স্পেসিফিকেশন, রিভিউ, স্টক হিস্ট্রি ইত্যাদি) কিন্তু লিস্টিং পেজে মাত্র ৪টি ফিল্ড দরকার হয়, তাহলে GraphQL over-fetching এড়াতে সুবিধাজনক — বিশেষ করে মোবাইলে। কিন্তু যদি প্রোডাক্ট অবজেক্ট ছোট হয় এবং প্রতিটি পেজের জন্য প্রায় একই ফিল্ড দরকার হয়, তাহলে REST-এর সরলতা ও CDN-ক্যাশিং সুবিধা (L20-এ বিস্তারিত) বেশি মূল্যবান হতে পারে।

  2. কোড এক্সটেন্ড করুন: উপরের কোড সেলে requested_fields বদলে শুধু ["email"] রাখুন এবং graphql_size কীভাবে বদলায় দেখুন। তারপর একটি নতুন প্রশ্ন যোগ করুন যা রেসপন্সে নেই এমন একটি ফিল্ড (যেমন "country") চায় — কী হয়?

    requested_fields = ["email"] দিলে GraphQL পেলোড আরও ছোট হবে (মাত্র ১টি ফিল্ড)। আর "country"-এর মতো একটি ফিল্ড চাইলে, যেহেতু আমাদের graphql_get_user ফাংশনে if f in full_record চেক আছে, সেটি নীরবে বাদ পড়ে যাবে (কোনো এরর ছাড়াই)। বাস্তব GraphQL সার্ভারে এটি একটি স্কিমা-ভ্যালিডেশন এরর হিসেবে ধরা পড়ত, কারণ স্কিমায় অনুমোদিত ফিল্ডের তালিকা আগে থেকেই নির্দিষ্ট থাকে।

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

পূর্ববর্তী পাঠ
HTTP/HTTPS ও REST API ডিজাইন