ধরো, তুমি একটা রেস্টুরেন্টে গেছো। চেয়েছো শুধু এক কাপ কফি আর এক টুকরো কেক। কিন্তু ওয়েটার পুরো থালি এনে হাজির: ভাত, মাংস, ডাল, সবজি, সালাদ। দরকার ছিল সামান্য, অথচ টেবিল ভরে গেছে এমন খাবারে যা তুমি ছুঁয়েও দেখবে না।
আবার উল্টোটাও হয়। তুমি কফি, কেক আর এক গ্লাস পানি একসাথে চাইলে, কিন্তু ওয়েটার প্রতিবার একটা করে জিনিস আনছে। তিনটা জিনিসের জন্য তিনবার আসা-যাওয়া।
প্রচলিত REST API অনেকটা এই ওয়েটারের মতো। প্রতিটি endpoint নির্দিষ্ট আকারের ডেটা ফেরত দেয়। তোমার যতটুকুই লাগুক, পুরো resource আসবে (overfetching)। আর একাধিক resource লাগলে একাধিক request পাঠাতে হবে (underfetching)।
GraphQL-এর কথা সহজ: "তুমি ঠিক কী চাও সেটা বলো, আমি ঠিক সেটুকুই দেবো। একবারেই দেবো।"
GraphQL-এর জন্মকথা
GraphQL-এর জন্ম ২০১২ সালে Facebook-এ (বর্তমানে Meta)। তখন তারা মোবাইল অ্যাপ নতুন করে বানাচ্ছিল। News Feed-এর মতো জটিল, nested ডেটা REST দিয়ে আনতে গিয়ে দেখা গেল অনেকগুলো request লাগছে, আর দুর্বল মোবাইল নেটওয়ার্কে অপ্রয়োজনীয় ডেটা আসছে। এই সমস্যার সমাধান হিসেবেই GraphQL তৈরি হয়।
২০১৫: Facebook GraphQL-এর specification ওপেন সোর্স করে।
২০১৮: Linux Foundation-এর অধীনে GraphQL Foundation গঠিত হয়। এখন এটা কোনো একক কোম্পানির না, একটা উন্মুক্ত standard।
আজ GitHub, Shopify, Netflix, Airbnb-সহ অনেক বড় প্রতিষ্ঠান GraphQL ব্যবহার করে।
GraphQL আসলে কী?
GraphQL হলো API এর জন্য একটা query language, আর সেই query চালানোর জন্য একটা server-side runtime। দুটো অংশই বোঝা জরুরি:
Query Language: client এই ভাষায় জানায় কোন ডেটা লাগবে, কী আকারে লাগবে।
Runtime: server-এ চলে। query পড়ে, schema অনুযায়ী যাচাই করে, তারপর প্রতিটি field-এর জন্য নির্দিষ্ট ফাংশন (resolver) চালিয়ে ডেটা জোগাড় করে।
একটা প্রচলিত ভুল ধারণা: GraphQL কোনো database না। SQL-এর মতো সরাসরি database-এ query চালায় না। এটা client আর server-এর মাঝখানের যোগাযোগের একটা স্তর। পেছনের ডেটা থাকতে পারে PostgreSQL, MongoDB, অন্য কোনো REST API বা microservice-এ।
GraphQL এর মূল বৈশিষ্ট্য
কোন field লাগবে, ঠিক করে client: response-এর আকার client-এর query অনুযায়ী হয়। তাই overfetching হয় না।
এক request-এ nested ও একাধিক resource: user, তার post, প্রতিটি post-এর comment, সব একবারে আনা যায়। তাই underfetching আর বারবার round-trip দূর হয়।
একটাই endpoint: REST-এ
/users,/posts,/commentsএরকম অনেক endpoint লাগে। GraphQL-এ সাধারণত একটাই থাকে:/graphql।Strongly typed schema: প্রতিটি field-এর type আগে থেকে নির্দিষ্ট, তাই ভুল query চালানোর আগেই ধরা পড়ে।
Introspection: schema নিজেই query করা যায়। এজন্য GraphiQL বা Apollo Sandbox-এর মতো টুল auto-complete আর ডকুমেন্টেশন দেখাতে পারে।
আগে থেকে অনুমানযোগ্য response: query যে আকারে লিখবে, JSON সেই আকারেই আসবে।
Schema: GraphQL-এর হৃদপিণ্ড
Schema হলো client আর server-এর মধ্যে একটা চুক্তি (contract)। কোন ডেটা আছে, কোন query চালানো যাবে, কোন পরিবর্তন করা যাবে, সব এখানে লেখা থাকে। Schema লেখা হয় SDL (Schema Definition Language) দিয়ে:
type User {
id: ID!
name: String!
email: String!
posts: [Post!]!
}
type Post {
id: ID!
title: String!
content: String
createdAt: String!
}
type Query {
user(id: ID!): User
}
type Mutation {
addPost(title: String!, content: String): Post!
}চিহ্নগুলোর অর্থ:
!(non-null): field কখনোnullহবে না।[Post!]!: লিস্ট নিজেও null হবে না, ভেতরের কোনো item-ও null হবে না।Scalar types: পাঁচটা built-in scalar আছে:
Int,Float,String,Boolean,ID। চাইলেDateTime-এর মতো custom scalar-ও বানানো যায়।Query,Mutation,Subscription: এই তিনটা বিশেষ root type। সব operation এদের যেকোনো একটা থেকে শুরু হয়।
GraphQL Query Structure
GraphQL query দেখতে অনেকটা JSON-এর মতো, তবে শুধু key থাকে, value নেই। value পূরণ করে server।
query {
user(id: "101") {
name
email
posts {
title
createdAt
}
}
}Response:
{
"data": {
"user": {
"name": "Farjanul",
"email": "farjanul@example.com",
"posts": [
{ "title": "Learning GraphQL", "createdAt": "2025-04-19" }
]
}
}
}query আর response-এর গঠন হুবহু এক। যা চেয়েছো তাই পেয়েছো, একটা field বেশিও না, কমও না।
বাস্তব প্রজেক্টে আরও কয়েকটা ফিচার কাজে লাগে:
Variables: query-তে মান সরাসরি না বসিয়ে
$userId-এর মতো variable ব্যবহার করা হয়। এতে একই query বারবার ব্যবহার করা যায়, আর injection-এর ঝুঁকিও কমে।Aliases: একই field আলাদা argument দিয়ে দুইবার আনতে চাইলে নাম বদলে নেওয়া যায়।
Fragments: বারবার লাগে এমন field-এর সেট একবার লিখে পুনরায় ব্যবহার করা যায়।
Directives:
@includeবা@skipদিয়ে শর্তসাপেক্ষে কোনো field রাখা বা বাদ দেওয়া যায়।
GraphQL-এর তিনটি প্রধান Operation
Query
ডেটা রিড করতে Query ব্যবহার হয়, REST-এর GET-এর মতো। Query-র কোনো side effect থাকা উচিত না, মানে server-এর ডেটা বদলাবে না। একটা query-তে একাধিক root field থাকলে server সেগুলো parallel-এ resolve করতে পারে।
Mutation
নতুন কিছু তৈরি, আপডেট বা ডিলিট করতে Mutation লাগে, REST-এর POST, PUT বা DELETE-এর মতো।
mutation {
addPost(title: "New Post", content: "Hello everyone") {
id
title
}
}এর দুটো গুরুত্বপূর্ণ দিক আছে। প্রথমত, পরিবর্তনের পর আপডেট হওয়া ডেটা একই response-এ চলে আসে, আবার fetch করতে হয় না। দ্বিতীয়ত, এক request-এ একাধিক mutation থাকলে সেগুলো ক্রমানুসারে চলে, parallel-এ না। এতে race condition এড়ানো যায়।
Subscription
রিয়েলটাইম ডেটার জন্য Subscription ব্যবহার হয়। যেমন chat message, stock price, লাইভ নোটিফিকেশন।
subscription {
newMessage {
content
sender
}
}Query আর Mutation সাধারণত HTTP দিয়ে চলে। Subscription-এর জন্য একটা দীর্ঘস্থায়ী সংযোগ লাগে, সাধারণত WebSocket বা Server-Sent Events। client একবার subscribe করে রাখে, তারপর নতুন event এলেই server নিজে থেকে ডেটা পাঠায়।
Resolver: পর্দার আড়ালে কী হয়?
Schema বলে কী ডেটা পাওয়া যাবে। resolver বলে সেটা কোথা থেকে, কীভাবে আনা হবে। প্রতিটি request এলে server চারটা ধাপে কাজ করে:
Parse: query পড়ে একটা syntax tree বানায়।
Validate: schema-র সাথে মিলিয়ে দেখে query বৈধ কিনা।
Execute: tree ধরে প্রতিটি field-এর resolver চালায়।
Response: ফলাফল query-র আকারে সাজিয়ে JSON পাঠায়।
Java তে (Spring for GraphQL) resolver দেখতে এরকম:
@Controller
public class UserController {
@QueryMapping
public User user(@Argument String id) {
return userRepository.findById(id);
}
// Runs only when the client asks for "posts"
@SchemaMapping(typeName = "User", field = "posts")
public List<Post> posts(User user) {
return postRepository.findByAuthorId(user.getId());
}
}এখানে একটা সূক্ষ্ম কিন্তু শক্তিশালী ব্যাপার আছে। client query-তে posts না চাইলে posts resolver চলবেই না। GraphQL শুধু নেটওয়ার্ক না, server-এর অপ্রয়োজনীয় কাজও বাঁচায়।
client-এর দিক থেকে GraphQL request আসলে একটা সাধারণ HTTP POST। Python দিয়ে:
import requests
query = "query($id: ID!) { user(id: $id) { name email } }"
response = requests.post(
"http://localhost:8000/graphql",
json={"query": query, "variables": {"id": "101"}},
)
print(response.json()["data"]["user"])বাস্তব উদাহরণ: ইউনিভার্সিটি অ্যাপ
ধরো, একটা ইউনিভার্সিটি অ্যাপের হোম স্ক্রিনে শুধু ছাত্রের নাম আর CGPA দেখাতে হবে। REST দিয়ে GET /students/101 কল করলে আসবে পুরো অবজেক্ট:
{
"id": 101,
"name": "Mominur",
"email": "mominur@brur.com",
"department": "CSE",
"cgpa": 3.5,
"address": { "city": "Dhaka", "zip": "1212" }
}লাগে দুটো field, এসেছে সাতটা। এটাই overfetching। একটা request-এ হয়তো তেমন ক্ষতি নেই, কিন্তু হাজার হাজার ব্যবহারকারী আর ধীর মোবাইল নেটওয়ার্ক হলে এই বাড়তি ডেটা ব্যান্ডউইথ, ব্যাটারি আর লোডিং টাইম তিনটাই খায়। GraphQL দিয়ে চাইবে শুধু যা দরকার:
query {
student(id: 101) {
name
cgpa
}
}এবার ধরো, প্রোফাইল পেজে ছাত্রের enrolled course আর প্রতিটি course-এর শিক্ষকের নামও লাগবে। REST-এ হয়তো তিন-চারটা আলাদা request লাগত (underfetching)। GraphQL-এ student query-র ভেতরেই courses { title teacher { name } } লিখে দিলে একটা request-এই সব চলে আসবে।
N+1 সমস্যা: GraphQL-এর লুকানো ফাঁদ
nested query-র সুবিধার একটা দামও আছে, যেটা অনেকে এড়িয়ে যায়। ধরো, ১০০ জন user আর প্রত্যেকের post আনতে চাও। প্রথমে ১টা query-তে user-রা আসবে। তারপর প্রতিটি user-এর জন্য posts resolver আলাদা করে database-এ query চালাবে। মোট ১ + ১০০ = ১০১টা query। ডেটা বাড়লে এটা server-কে মারাত্মক ধীর করে দেয়।
সমাধানের নাম DataLoader (batching)। প্রতিটি user-এর জন্য আলাদা query না চালিয়ে সব user-এর ID একসাথে জড়ো করা হয়, তারপর একটা query চালানো হয় (WHERE author_id IN (...))। Spring for GraphQL-এ @BatchMapping, আর Python-এর Strawberry-তে DataLoader দিয়ে এটা করা যায়। Production-এ GraphQL চালালে এটা প্রায় বাধ্যতামূলক।
Error Handling: REST থেকে আলাদা
REST-এ error বোঝা যায় HTTP status code দেখে (404, 500)। GraphQL-এ request প্রসেস হলে সাধারণত HTTP 200 আসে, এমনকি ভেতরে কোনো field fail করলেও। error থাকে response-এর errors array-তে, সাথে কোন field-এ সমস্যা হলো সেই path।
এর মানে GraphQL-এ partial success সম্ভব: কিছু ডেটা আসবে, কিছু field-এ error থাকবে। তাই client-এ সবসময় data আর errors দুটোই চেক করতে হয়।
GraphQL vs REST
বিষয় | REST | GraphQL |
|---|---|---|
Structure | প্রতিটি resource-এর আলাদা endpoint | সাধারণত একটাই endpoint, query dynamic |
Data fetching | overfetching ও underfetching প্রায়ই হয় | client ঠিক যা দরকার তাই নেয় |
Nested resource | সাধারণত একাধিক request লাগে | এক request-এ nested ডেটা আসে |
Versioning | প্রায়ই | নতুন field যোগ আর পুরনো field |
Type system | ঐচ্ছিক (OpenAPI দিয়ে করা যায়) | strongly typed schema বাধ্যতামূলক |
Documentation | আলাদা করে লিখতে হয় | introspection থেকে স্বয়ংক্রিয়ভাবে আসে |
HTTP caching | সহজ, CDN-friendly | কঠিন, কারণ সব request একই URL-এ |
Error handling | HTTP status code দিয়ে |
|
Performance | সহজ, cache-যোগ্য request-এ খুব দ্রুত | round-trip ও payload কম, কিন্তু query parsing আর N+1-এর ঝুঁকি আছে |
শেখার সহজতা | সহজ, বহুল পরিচিত | schema, resolver, DataLoader শিখতে হয় |
একটা সতর্কতা: "GraphQL সবসময় REST-এর চেয়ে দ্রুত", এটা ঠিক না। GraphQL network round-trip আর payload কমায়, মোবাইলে যেটা বড় সুবিধা। কিন্তু resolver ঠিকমতো লেখা না হলে উল্টো ধীর হতে পারে। পারফরম্যান্স আসলে implementation-এর মানের ওপর নির্ভর করে।
Security নিয়ে কিছু জরুরি কথা
GraphQL client-কে অনেক স্বাধীনতা দেয়, আর সেই স্বাধীনতার অপব্যবহারও সম্ভব। যেমন কেউ user → posts → author → posts → author... এভাবে চক্রাকারে গভীর query পাঠিয়ে server বসিয়ে দিতে পারে। তাই এই ব্যবস্থাগুলো জরুরি:
Query Depth Limiting: query কত স্তর গভীর হতে পারবে তার সীমা বেঁধে দেওয়া।
Query Complexity Analysis: প্রতিটি field-কে একটা "খরচ" দিয়ে মোট খরচ সীমা ছাড়ালে query বাতিল করা।
Rate Limiting: শুধু request-এর সংখ্যা না, query-র complexity অনুযায়ীও সীমা দেওয়া।
Pagination বাধ্যতামূলক: লিস্ট field-এ limit ছাড়া হাজার হাজার রেকর্ড ফেরত দেওয়া যাবে না।
Authentication: JWT, OAuth 2.0 ইত্যাদি দিয়ে পরিচয় যাচাই।
Field-level Authorization: কে কোন field দেখতে পারবে তার নিয়ন্ত্রণ। যেমন CGPA দেখতে পারবে শুধু ছাত্র নিজে আর admin।
Production-এ introspection বন্ধ রাখা: নইলে যে কেউ পুরো schema দেখে ফেলতে পারবে।
Persisted Queries: client শুধু আগে থেকে অনুমোদিত query পাঠাতে পারবে।
জনপ্রিয় GraphQL Tools ও লাইব্রেরি
Platform | Tool |
|---|---|
JavaScript / Node.js | Apollo Server, GraphQL Yoga, Apollo Client |
Python | Strawberry, Graphene, Ariadne |
Java | Spring for GraphQL, graphql-java, Netflix DGS |
PHP | Lighthouse (Laravel), webonyx/graphql-php |
Go | gqlgen |
Frontend / Mobile | Relay (React), Apollo iOS, Apollo Kotlin |
Testing ও Exploration | GraphiQL, Apollo Sandbox, Postman |
কখন GraphQL ব্যবহার করবে?
যদি তোমার API-তে এরকম পরিস্থিতি থাকে:
বিভিন্ন client (web, mobile, smartwatch) ভিন্ন ভিন্ন field চায়।
মোবাইলে ব্যান্ডউইথ আর লোডিং টাইম বাঁচাতে হবে।
একটা স্ক্রিনের জন্য একাধিক resource একসাথে লাগে।
Frontend ঘন ঘন বদলায়, আর প্রতিবার backend-এ নতুন endpoint বানাতে চাও না।
অনেকগুলো microservice-এর ডেটা একটা জায়গা থেকে দিতে চাও (API Gateway বা Federation)।
তাহলে GraphQL খুব ভালো পছন্দ।
কখন REST বেটার হতে পারে?
ছোট বা সাধারণ CRUD API, যেখানে GraphQL-এর বাড়তি জটিলতা লাভের চেয়ে বেশি।
HTTP caching আর CDN খুব গুরুত্বপূর্ণ (REST-এ URL-ভিত্তিক caching অনেক সহজ)।
বড় ফাইল upload বা download বেশি।
Public API বা third-party integration, যেখানে সবাই REST-এ অভ্যস্ত।
Legacy system, যেখানে নতুন স্তর যোগ করার যুক্তি নেই।
অনেক প্রতিষ্ঠান দুটোই একসাথে ব্যবহার করে। যেখানে যেটা মানায়, সেখানে সেটা।
GraphQL মানেই Power to the Client
GraphQL হলো flexibility, efficiency আর developer control-এর সুন্দর মিলন। REST এখনো শক্তিশালী আর বহুল ব্যবহৃত। তবে জটিল, ডেটা-নির্ভর অ্যাপ্লিকেশনের ক্ষেত্রে GraphQL তাকে জোরালো টেক্কা দিচ্ছে।
GraphQL gives clients the power to shape the data they need — no more, no less.
GraphQL শিখলে তুমি শুধু আরেকটা API টুল শিখবে না। শিখবে ডেটা কীভাবে চাইতে হয়, কীভাবে সাজাতে হয়, আর কীভাবে client ও server-এর মধ্যে একটা পরিষ্কার চুক্তি তৈরি করতে হয়। এটা শুধু একটা টেকনোলজি না, ডেটা নিয়ে ভাবার একটা নতুন mindset।