HATEOAS কি?

HATEOAS এর পূর্ণরূপ হচ্ছে Hypermedia As The Engine Of Application State. কত বড় নাম রে বাবা! খুব ভয়াবহ টাইপের কিছু একটা মনে হচ্ছে না? মনে হচ্ছে খুব কঠিন, জটিল জিলিপির প্যাঁচ বোধহয় এটার মধ্যে লুকিয়ে আছে। তাই না? আসুন দেখি সহজভাবে এটা বোঝা যায় কিনা। তাঁর আগে বলে নেই, এটা মূলত API ডেভেলপমেন্ট এবং REST এর সাথে রিলেটেড একটা টপিক। সুতরাং এই বিষয়ে যাদের আগ্রহ নেই তাঁরা বিদায় নিতে পারেন, কারণ এই আর্টিকেল আপনাদের কাছে বোরিং লাগতে পারে।

তো শুরু করা যাক।

আপনি নিশ্চয়ই জানেন যে, আমরা যখন কোনো একটা সাইটে ঢুকি বা কোনো ওয়েবপেইজ ব্রাউজ করি তখন মূলত আমরা ঐ পেইজের URL দিয়ে সার্ভারের কাছে ঐ পেইজের কন্টেন্ট রিকোয়েস্ট করি, আর সার্ভার রেসপন্স হিসেবে আমাদের কাছে সেই কন্টেন্ট পাঠিয়ে দেয়। মনে করেন, আপনি কোনো একটা কোম্পানীর ওয়েবসাইটের হোমপেইজ এ গেলেন http://awesomecompany.com লিঙ্কটা ব্রাউজারে টাইপ করে। সুন্দর একটা পেইজ আপনার ব্রাউজারে ওপেন হলো। এখন আপনি এই কোম্পানীর About দেখতে চান, তখন কি আপনি http://awesomecompany.com/about আবার ব্রাউজারে লিখে টাইপ করে রিকোয়েস্ট করেন? না, কারণ আপনার প্রথমে রিকোয়েস্ট করা পেইজেই হয়তো একটা লিংক আছে যেখানে ক্লিক করলেই আপনাকে About পেইজে নিয়ে যাবে। তারমানে আমরা যখন ওয়েবে ব্রাউজ করি তখন আমরা এক পেইজ থেকে আরেক পেইজের লিংকে ক্লিক করে চলে যাই।

আচ্ছা, এবার দেখি আমরা যখন RESTful API বানাই তখন কি হয়। আমরা তো জানি যে, RESTful API তে আমরা একেকটা রিসোর্সকে মূলত একেকটা Endpoint হিসেবে সাজাই। তো আমাদের যদি একটা User রিসোর্স থাকে, সবগুলো ইউজারের লিস্ট পাওয়ার জন্য আমরা হয়তো http://api.awesomecompany.com/users এই Endpoint এ একটা GET রিকোয়েস্ট করবো। তাতে করে অনেকটা নীচের মত একটা রেসপন্স আমরা পাবো যেখানে ইউজারদের তালিকা থাকবে।

[
{
"id": 1,
"name": "Adu vai",
"email": "adu.vai@gmail.com"
},
{
"id": 2,
"name": "Bodu vai",
"email": "bodu.vai@gmail.com"
},
....
....
....
]

আচ্ছা, এখন যদি প্রথম ইউজারের Details দেখতে চাই, তাহলে আমরা REST এর স্পেসিফিকেশন অনুযায়ী জানি যে, http://api.awesomecompany.com/users/1 এই Endpoint এ একটা GET রিকোয়েস্ট করতে হবে। এখানে ছোট্ট করে একটু বলে নেই, আপনি যদি এই API এর ক্লায়েন্ট হন, অর্থাৎ এই API কে ব্যবহার করে যদি আপনি কোনো এপ্লিকেশন এর ফ্রন্টএন্ড বা মোবাইল এপ্লিকেশন বানান, তাহলে এই API এর কি কি Endpoint আছে সেটা এই API এর ডকুমেন্টেশন পড়ে আপনাকে জানতে হবে এবং সেই অনুযায়ী Endpoint URL গুলো আপনার কোডে হার্ডকোড করে দিতে হবে। আবার লিস্ট এর JSON রেসপন্স থেকে ইউজারের ID নিয়ে http://api.awesomecompany.com/users/1 এই ধরনের URL গুলো রানটাইমে জেনারেট করে নিতে হবে।

আপনি যদি REST নিয়ে কাজ করে থাকেন তাহলে এইটুকু পর্যন্ত আপনি ইতোমধ্যেই জানেন।

এবার আমরা একটু ভিন্ন একটা পদ্ধতি অবলম্বন করবো আমাদের API তে। এখন আমাদের API তে User রিসোর্সের লিস্টের জন্য আগের মতই কল করলে আমাদের রেসপন্সটা হবে নীচের মতঃ

[
{
"id": 1,
"name": "Adu vai",
"email": "adu.vai@gmail.com",
"_links": [
{
"rel": "self",
"href": "http://api.awesomecompany.com/users/1"
}
]
},
{
"id": 2,
"name": "Bodu vai",
"email": "bodu.vai@gmail.com",
"_links": [
{
"rel": "self",
"href": "http://api.awesomecompany.com/users/2"
}
]
},
....
....
....
]

অ কি মনু, User এর মইদ্যে এই _links আইলে আবার কোম্মেইথ্যা?

এখানেই হচ্ছে মজা। দেখেন, আমরা প্রত্যেকটা User এর ডাটার সাথে তাঁর কিছু লিংকের একটা লিস্ট দিয়ে দিয়েছি। এখানে আপাতত যেই একটা লিঙ্ক অবজেক্ট দেয়া আছে সেখানে ২ টা প্রপার্টি আছে,
১। rel: যেটা এই লিংকের সাথে রিসোর্সের relation কি সেটা বলে।
২। href: একটা লিংক বলে দেয়। যেহেতু rel এ বলা আছে self তারমানে এই লিংকটা রিসোর্সটার নিজের লিংক বা তার ডিটেইলস পাওয়ার লিংক।

এখন এই পদ্ধতিতে লাভ কি হলো? লাভ হলো যে, আপনার ক্লায়েন্ট কোডে আলাদা করে User এর ডিটেইলস এর জন্য লিংক রাখা বা লিস্টের রেস্পন্সের User ডাটা থেকে id নিয়ে তাঁর ডিটেইলসে যাওয়ার লিঙ্ক জেনারেট করতে হচ্ছে না। কারণ, সেই লিংক API থেকেই দিয়ে দিচ্ছে। এই যে আমরা কাজটা করলাম, এটাই মূলত HATEOAS ফলো করে বানানো API. এখানে প্রত্যেকটা রিসোর্সের সাথে সেই রিসোর্সের উপর কি কি কাজ করা যাবে এবং সেই রিলেটেড লিংকগুলো দিয়ে দেয়া থাকে।

আসল মজাটা কি বুঝতে পেরেছেন? এইভাবে যদি আমরা API বানাই তাহলে আমাদের API টাকে মূলত ওয়েবপেইজগুলো যেভাবে লিংক থেকে লিংকে ব্রাউজ করা যায় অনেকটা সেভাবে ব্যবহার করা যাবে।

এই ব্যাপারটাকে আরেকটু সুন্দরভাবে ব্যাখ্যা করা যায় Finite State Machine এর কন্সেপ্ট দিয়ে। Finite State Machine কি জিনিস যদি জানা না থাকে তাহলে ছোট্ট করে একটু বলে নেই। Finite State Machine এ মূলত কিছু State আর তাঁর সাথে প্রতিটা State থেকে অন্য কোন State এ যাওয়া যাবে (যেটাকে বলে transition) সেটা ডিফাইন করা থাকে। প্রতিটা ট্রান্সজিশনে স্টেট পরিবর্তন হয় এবং কোনো স্টেট থেকে শুধুমাত্র যেই ট্রানজিশন বলে দেয়া থাকে সেগুলোতেই যাওয়া যায়, অন্যগুলোতে না। যেমন একটি উদাহরণ দেয়া যাকঃ

তীর চিহ্ন দিয়ে Transition দেখানো হয়েছে। গোল বৃত্তগুলো State

Finite State Machine দিয়ে অনেক কিছুই ব্যাখ্যা করা যায়। এখানে উদাহরণ দেয়ার জন্য একটা হরিণের দিনলিপি বোঝানোর চেষ্টা করা হয়েছে। দেখুন যে, হরিণ কিন্তু যখন ঘুমে আছে তখন সে সরাসরি খাওয়া দাওয়া স্টেটে যেতে পারবে না, কারণ আগে তাঁর ঘুম ভাংতে হবে এবং খাবার খুঁজতে হবে। উপরের ছবি দেখে বাকিগুলোও
বুঝতে কষ্ট হওয়ার কথা না কীভাবে এক স্টেট থেকে আরেক স্টেটে ট্রানজিশন হয়। তাই না? আপনি এরকম স্টেট মেশিন দিয়ে আপনার প্রোগ্রামিঙয়ের লজিক ফ্লো, এলগোরিদম ইত্যাদিও ব্যাখ্যা করতে পারবেন। যাই হোক, এ নিয়ে বিস্তারিত আরেকদিন।

যা বলছিলাম, ওয়েবপেইজ এ আমরা হাইপারলিংক ব্যবহার করে এক পেইজ থেকে আরেক পেইজে যে ব্রাউজ করি সেটার সাথে উপরের স্টেট মেশিনের ট্রানজিশনের কি মিল পাওয়া যায়? শুরু করলাম হোমপেইজ থেকে, সেখান থেকে একটা লিংকে ক্লিক করে কোনো একটা পেইজে সেখান থেকে আরেকটা পেইজে। একই রকম না?
আমরা HATEOAS ফলো করে API তে যেরকম রেসপন্স দিচ্ছি সেটা ব্যবহার করেও কিন্তু এই রকম transition কিংবা navigate করা যাবে। প্রত্যেকটা রেসপন্সে আমরা যে রিসোর্স পাচ্ছি সেগুলো হচ্ছে Application State আর তাঁর সাথে যেই লিংকগুলো দেয়া আছে সেগুলো ব্যবহার করে করা প্রতিটা একশন হবে একেকটা Transition. তারমানে API ব্যবহারের পুরো ফ্লো-টাকে আমরা অনেকটা Finite State Machine এর মত করে ফেলেছি। তারমানে ব্যাপারটা দাঁড়াচ্ছে যে, এপ্লিকেশনের ওয়ার্ক-ফ্লো টা আমরা মূলত API রেসপন্স থেকেই পাবো। রানটাইমে ক্লায়েন্ট API রেসপন্সের উপর ভিত্তি করে ডিসিশন নিতে পারবে তাঁর কি কি করার সুযোগ আছে। যদি এই ফ্লো-তে পরবর্তীতে কিছু চেঞ্জ হয় তাহলে ক্লায়েন্ট এপ্লিকেশনে খুব বড় ধরনের কোনো ধাক্কা খাবে না। যদিও নতুন ফ্লো-য়ের জন্য কিছু কাজ করতেই হবে, তারপরও সেটা আগের তুলনায় অনেক কম হবে। মজার না ব্যাপারটা?

এতক্ষণে তো নিশ্চয়ই, Hypermedia As The Engine Of Application State এর অর্থ বোঝা গেছে, তাই না? Hypermedia বলতে সাধারণত এমন কন্টেন্টকে বোঝায় যেগুলোর মধ্যে অন্য কন্টেন্টের লিংক থাকে, সে হিসেবে আমাদের রিসোর্সগুলোই Hypermedia হবে আর এগুলো দিয়েই আমরা Application State কে রিপ্রেজেন্ট করা এবং তাঁদের মধ্যে Transition কি হবে তা বলে দিতে পারবো।

আপনি যদি PHP এর Laravel ফ্রেমওয়ার্ক নিয়ে কাজ করে থাকেন তাহলে নিশ্চয়ই দেখেছেন যে কোনো একটা লিস্টকে paginate করলে যেই JSON রেসপন্স পাওয়া যায় তাতে এই রকম links সহ আরো কিছু meta ডাটা দিয়ে দেয়। সেটা কিন্তু মূলত এই HATEOAS ফলো করেই। অন্য কোনো লাইব্রেরী/ফ্রেমওয়ার্কে এরকম করে কিনা আমার জানা নেই, করতেও পারে। গিটহাবের API তে কোনো স্পেসিফিক ইউজারের ডিটেইলস এর জন্য রিকোয়েস্ট করলে এই রকম বেশ কিছু লিংক দিয়ে দেয়। এখানে ক্লিক করে দেখতে পারেন

এখানে ছোট আরেকটা ব্যাপার বলে রাখি। Leonard Richardson নামে এক ভদ্রলোক REST এর জন্য একটা maturity model প্রপোজ করেছেন। এই মডেল দিয়ে ডিফাইন করা যায় যে, কোনো একটা API এর ইমপ্লিমেন্টেশন কোন লেভেল এর RESTful সেটা বোঝা যায়। ঐ মডেলের ৪ টা ভাগ আছেঃ

  • Level-0 (Swamp of POX): এই লেভেলের API তে একটাই মাত্র Endpoint থাকে এবং ক্লায়েন্ট তাঁর সব রিকোয়েস্ট এই একটা Endpoint এই করে (সাধারণত একটা নির্দিষ্ট HTTP verb, যেমন POST, ব্যবহার করে)। এখানে রিসোর্স বলে আলাদা কিছু নেই। এই একটা Endpoint এর রিকোয়েস্টের payload এ ক্লায়েন্ট বলে দেয় সে কি চায়, সেটার উপর ভিত্তি করেই API তাকে রেসপন্স দেয়। যেমনঃ আমাদের ব্লগ API এর জন্য Endpoint হতে পারে http://api.awesomeblog.com/blogservice
    ক্লায়েন্ট এটা ছাড়া আর কোনো Endpoint চিনবে না বা অন্য কোনো URL এ রিকোয়েস্ট করবে না।
  • Level-1 (Resource): এই লেভেলের API তে প্রত্যেকটা রিসোর্সের জন্য আলাদা আলাদা Endpoint হবে এবং এখানেও সব রিকোয়েস্ট মূলত একটা HTTP verb, যেমন POST, ব্যবহার করেই হবে। এটার সাথে Level-0 এর পার্থক্য হলো এখানে User রিসোর্সের জন্য http://api.aswesomeblog.com/users, পোস্টের জন্য http://api.awesomeblog.com/posts এরকম রিসোর্সগুলোর জন্য আলাদা আলাদা Endpoint থাকবে।
  • Level-2 (HTTP Verbs): আমরা সাধারণত যেভাবে REST API বানাই সেটা মূলত এই লেভেলেরই। এই লেভেলের API তে আলাদা আলাদা রিসোর্স Endpoint যেমন থাকে, তেমনি সেই রিসোর্সগুলোতে এক্সেস করার জন্য HTTP verb গুলোর যথাযথ প্রয়োগ করা হয়। যেমন, রিসোর্স fetch করার জন্য GET, create করার জন্য POST, update করার জন্য PUT/PATCH ব্যবহার করা হয়।
  • Level-3 (Hypermedia controls): এটাই মূলত HATEOAS ব্যবহার করে বানানো API. যেখানে রিসোর্সগুলোর ডাটার সাথে Hypermedia প্রোভাইড করা হয় যাতে করে সেগুলো দিয়ে রিসোর্সগুলোর উপর একশন নির্ধারণ করা যায়।

এ ব্যাপারে আরো বিস্তারিত জানতে পড়ে ফেলুন Martin Fowler এর এই লেখাটি

HATEOAS কোনো টুলস না, এটা মূলত REST এরই কিছু constraint। REST এর মূল যেই প্রপোজালটা Roy Fielding তাঁর ডিজার্টেশন পেপারে দেন সেখানে এটারও উল্লেখ ছিলো। আপনার API যদি আপনি এভাবে বানান যেখানে এই রকম লিংক থাকবে যেগুলো অন্য রিলেটেড API Endpoint কে লিংক করবে তাহলে বলা যাবে যে, আপনার API টা HATEOAS-compliant. এটাকে অবশ্য Hypermedia API ও বলা হয়।

এখনো হয়তো একটা ব্যাপার পুরোপুরি ক্লিয়ার নাও হতে পারে অনেকের কাছে যে এই যে লিংকগুলো সেগুলো আসলে কি কি হবে? মানে একটা রিসোর্সের মধ্যে কি কি লিংক হতে পারে? লিংকগুলো কি কি হবে বা লিংকগুলো কীভাবে ডিফাইন হবে এটার আসলে এখনো কোনো স্বীকৃত ইউনিভার্সাল ফরম্যাট নেই। তবে যে কনভেনশনগুলো আছে তাঁর মধ্যে JSON HAL অন্যতম (যদিও এটাও এখনো পুরোপুরি স্ট্যান্ডার্ড না)।

উপরের উদাহরণটা বেশী সিম্পল ছিলো। আরেকটু কমপ্লেক্স একটা উদাহরণ নীচে দেই।

মনে করেন আপনি একটা ব্লগের জন্য API বানাচ্ছেন। Post রিসোর্সের লিস্টের জন্য আপনার API রেসপন্স নীচের মত হতে পারেঃ

{
"data": [
{
"id": 1,
"title": "Hello world",
"description": "lorem ipsum dolor sit amet",
"author": 10,
"_links": [
{
"rel": "self",
"type": "GET",
"href": "http://api.awesomeblog.com/posts/1"
},
{
"rel": "self",
"type": "PATCH",
"href": "http://api.awesomeblog.com/posts/1"
},
{
"rel": "self",
"type": "DELETE",
"href": "http://api.awesomeblog.com/posts/1"
},
{
"rel": "comments",
"type": "GET",
"href": "http://api.awesomeblog.com/comments"
},
{
"rel": "author",
"type": "GET",
"href": "http://api.awesomeblog.com/users/10"
}
]
}
....
....
....
],
"meta": {
"total": 50,
"per_page": 15,
"current_page": 1,
"last_page": 4,
"_links": [
{
"rel": "next",
"href": "http://api.awesomeblog.com/posts?page=2"
},
{
"rel": "prev",
"href": null
}
]
}
}

এখানে একটু খেয়াল করলে দেখবেন যে, প্রতিটা Post এর ডাটার সাথে তাঁর রিলেটেড GET, PATCH, DELETE একশন গুলোর টাইপসহ লিংক দিয়ে দেয়া হয়েছে। এছাড়াও পোস্টের author বা comments পাওয়ার জন্যও লিংক দেয়া আছে। আর শেষে দেয়া আছে pagination রিলেটেড ডাটা। এখন মনে করুন আপনি যখন পোস্টগুলোর লিস্ট ক্লায়েন্ট সাইডে দেখাবেন তখন সাধারণভাবে যেই যেই অপারেশনগুলোর সুযোগ আপনি ইউজারকে দিতে চান (যেমনঃ রিলেটেড কমেন্টসগুলো লোড করে দেখানো, author এর প্রোফাইলে যাওয়া, পোস্ট এডিট-ডিলিট করা) সেগুলো আপনি এই রেসপন্স থেকেই ডাইনামিক্যালি জেনারেট করতে পারবেন।

HATEOAS এর সুবিধাগুলোর মধ্যে অন্যতম হলোঃ

  • URL structure পুরোপুরিভাবে সার্ভার নিয়ন্ত্রন করে। ক্লায়েন্ট সাইডে হার্ডকোড করে URL মেইন্টেইন করতে হয় না।
  • ক্লায়েন্ট সাইডে API documentation দেখে দেখে URL generate করার লজিক ইমপ্লিমেন্ট করতে হয় না।
  • ক্লায়েন্ট ডেভেলপারদের পুরোপুরি API documentation এর উপর নির্ভর করতে হয় না, কারণ API নিজেই তখন self-explorable.
  • API versioning করা সহজ হয় এবং তাঁর প্রভাবে ক্লায়েন্ট সাইডে তেমন একটা চেঞ্জ করতে হয় না।

আরো বেশকিছু সুবিধা আছে, বিস্তারিত জানতে এই লিংকে ঢুঁ মেরে আসুন।

যাই হোক, যদিও বোঝার জন্য HATEOAS আসলে তেমন জটিল কিছু না কিন্তু ইমপ্লিমেন্ট করার ক্ষেত্রে কিছু জটিলতা বাড়ে বলে অনেকেই এটা ফলো করতে চান না। অন্য আরো অনেক বিষয়ের মত এটার ব্যাপারেও পক্ষে-বিপক্ষে অনেক মত আছে, সেটা নিয়ে আসলে খুব বেশী কথা বলে লাভ নেই। এটা যে ফলো করতেই হবে এমন বাধ্যবাধকতা নেই। আগ-পিছ চিন্তা করে সব মিলিয়ে যদি এটা বেটার সলিউশন মনে হয় তাহলে ব্যবহার করা যেতে পারে। জানেন নিশ্চয়ই, Life is all about Trade-offs.

শিখে রাখলে ক্ষতি নেই, তাই নিজেও একটু ঘাঁটাঘাঁটি করলাম আর নিজের বুঝ পাকাপোক্ত করার জন্য এই আর্টিকেলটা লিখে ফেললাম। ভুলভাল কিছু বলে থাকলে ধরিয়ে দিতে ভুলবেন না যেন।

--

--

Ahmed shamim hassan
প্রোগ্রামিং পাতা

Curious mind. Software Engineer. Polyglot programmer. Avid learner. Average person.