Why HATEOAS
Upcoming SlideShare
Loading in...5
×
 

Why HATEOAS

on

  • 23,965 views

A simple case study around HATEOAS (Hypermedia as the engine of application state), an essential constraint of the REST architecture style

A simple case study around HATEOAS (Hypermedia as the engine of application state), an essential constraint of the REST architecture style

Statistics

Views

Total Views
23,965
Views on SlideShare
20,355
Embed Views
3,610

Actions

Likes
55
Downloads
298
Comments
5

54 Embeds 3,610

http://odino.org 2031
http://ws-dl.blogspot.com 597
http://clwiki.cachelogic.com 217
http://trilancer.wordpress.com 148
http://odino.github.com 88
https://jabbr.net 81
http://ws-dl.blogspot.de 39
http://confluence.alm.mentorg.com:8090 38
http://ws-dl.blogspot.ca 36
http://ws-dl.blogspot.co.uk 36
http://ws-dl.blogspot.in 30
http://ws-dl.blogspot.fr 27
http://127.0.0.1 26
http://ws-dl.blogspot.com.br 21
http://ws-dl.blogspot.nl 19
http://www.slideshare.net 19
http://confluence.alm.mentorg.com 17
https://twitter.com 16
http://ws-dl.blogspot.com.es 11
http://ws-dl.blogspot.co.at 9
http://ws-dl.blogspot.se 8
http://ws-dl.blogspot.jp 7
http://ws-dl.blogspot.ch 6
http://ws-dl.blogspot.ro 6
http://cloud.feedly.com 6
http://ws-dl.blogspot.ru 6
http://ws-dl.blogspot.fi 5
http://ws-dl.blogspot.co.il 5
http://ws-dl.blogspot.com.au 5
http://www.blogger.com 4
http://ws-dl.blogspot.it 4
http://ws-dl.blogspot.kr 4
http://intra.cachelogic.com 4
http://ws-dl.blogspot.no 3
http://ws-dl.blogspot.com.tr 2
http://ws-dl.blogspot.be 2
http://flower-inwoven25.rssing.com 2
http://www.google.co.uk 2
http://ws-dl.blogspot.ie 2
http://ams.activemailservice.com 2
http://ws-dl.blogspot.co.nz 2
http://clwiki.corp.velocix.com 2
http://ws-dl.blogspot.gr 2
http://ws-dl.blogspot.com.ar 2
https://si0.twimg.com 2
http://geekolator.wordpress.com 1
http://ws-dl.blogspot.cz 1
http://translate.googleusercontent.com 1
http://ws-dl.blogspot.hu 1
http://geekolator.com 1
More...

Accessibility

Upload Details

Uploaded via as Adobe PDF

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
  • This is the most eye opening deck I've seen on the subject of RESTful APIs, and brings back a lot of painful memories of systems I've built which fell into the same trap. My next API will almost certainly follow these guidelines. Thank you OP.
    Are you sure you want to
    Your message goes here
    Processing…
  • Very well explanation and great use of visuals.
    Are you sure you want to
    Your message goes here
    Processing…
  • The best doc about Hateoas I've seen. thanks!
    Are you sure you want to
    Your message goes here
    Processing…
  • 3 years after this presentation and I don't see HATEOAS widely adopted. Why?
    Are you sure you want to
    Your message goes here
    Processing…
  • If your entry points change, you should redirect the user to the new default entry point. In HTTP-speak, that's one of the class 3xx status codes.

    That should give you time to depreciate and notify your clients of changes.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

Why HATEOAS Why HATEOAS Presentation Transcript

  • WHY HATEOAS A simple case study on the often ignored REST constraint Wayne Lee, June 2009 http://trilancer.wordpress.com/
  • Background • Representational state transfer (REST) – A software architecture style for distributed hypermedia systems, e.g., the World Wide Web – Introduced by Roy Fielding in 2000 • REST Constraints – Identification of resources – Manipulation of resources through representations – Self-descriptive messages – Hypermedia as the engine of application state
  • HATEOAS Hypermedia as the engine of application state • The model of application is an engine that moves from one state to another by picking alternative state transitions in current set of representations • Or simply put: – State: where the user is • i.e., current resources – Transitions: instructions on user’s next steps • i.e., links / forms from current resources to others
  • Why HATEOAS • Resources will evolve over time – Naming, URI, location, partition … • Thus any assumptions about server resources will break eventually – URI pattern, valid state transitions … • HATEOAS is about reducing assumptions – Loose coupling between client & server – Allowing each to evolve independently
  • A Simple Case Study Imagine You’re Building an Online Order Manager
  • Where You Start Users Table Orders Table ID Name ID User_ID 1 Tom 123 1 2 Jerry 456 2 … … … … Server 1: myorders.com • One server • Two DB tables • Free user registration
  • Your “REST” API V1 • Step 1: POST /login (user_name, password) – Session created • Step 2: GET /orders – Get Order List, with user id implicitly provided in session • Step 3: GET /orders/{order_id} – Get specific Order data through cooked URI • Sample order list data in JSON format: [ order: {id:123}, order: {id:456}, ] • URI cooking rules: – List_URI = ‘/orders’ – Order_URI = List_URI + order_list[n].order.id • Seems really simple for client app implementation, for now
  • Then lots client apps emerge … • From users, fans, solution providers, mashup makers… • 10s  100s  1000s … • Based on the simple “REST” API V1
  • After some time … • Some suggest implicit user id in session / cookie NOT a good idea … • That user_name should be included in URI
  • “REST” API V1.1 • Step 1: POST /login (user_name, password) – Session created • Step 2: GET /{user_name}/orders – Get Order List, with user name explicitly provided • Step 3: GET /{user_name}/orders/{order_id} – Get specific Order data through cooked URI • URI cooking rules: – User_name retrieved from client local input – List_URI = “/” + user_name + “/orders” – Order_URI = List_URI + order_list[n].order.id • Seems simple for client implementation still
  • But, what about old apps? • Just let them break? – Not Acceptable • Make sure Orders servlet maintain backward compatibility: – Retrieve user_name from request URI – If NOT provided, retrieve from session data instead • In the end, API V1 still works for old apps
  • Then after some time … • You decide to add a paid offerings: – Free accounts: • Data on the old host – Professional accounts: • Data moved to a new faster server • With a new domain name
  • DB Changes Users Table Orders Table for Free Users ID Name Type ID User_ID 1 Tom Free 123 1 2 Jerry Pro … … … … … Server 1: myorders.com Orders Table for Pro Users ID User_ID 456 2 … … Server 2: pro.myorders.com
  • “REST” API V2 • Step 1: POST /login (user_name, password) – Session created, with User_Type returned • Step 2: – Free accounts: GET /{user_name}/orders – Pro accounts: GET pro.myorders.com/{user_name}/orders • Step 3: – Free accounts: GET /{user_name}/orders/{order_id} – Pro accounts: GET pro.myorders.com/{user_name}/orders/{order_id} • URI cooking rules: – User_name retrieved from client-side input – User_type received from server, “free” or “pro” – List_URI = ((user_type == ‘pro’) ? ‘pro.myorders.com/’ : ‘/’) + user_name + ‘/orders’ – Order_URI = List_URI + order_list[n].order.id • Still ok for client implementation, nonetheless
  • Again, what about old apps? • Just let them break? – Still Not Acceptable • Modify Orders servlet logic again: – Retrieve domain & user_name from request URI – If NOT provided  API V1.0 • Retrieve user_name from session first, then • Lookup Users table to determine user_type, i.e., which DB to use – If only user_name provided  API V1.1 • Likewise, lookup Users table to determine which DB to use • In the end, API V1/V1.1 still works fine
  • As time goes by • You think it time for a VIP offering: – Free accounts: • Data on the old host – Professional accounts: • Data on a faster server • With a new domain name – VIP accounts: • Dedicated DB server • Custom domain name
  • DB Changes Users Table Orders Table for Free Users ID Name Type Domain ID User_ID 1 Tom Free N/A 123 1 2 Jerry Pro N/A … … 3 Susan VIP susan_test Server 1: myorders.com Orders Table for Pro Users Orders Tables for VIP User ID User_ID ID data 456 2 789 … … … … … Server 2: pro.myorders.com Server 3: susan_test.myorders.com Server 4: mikeabc.myorders.com Server 5: Alf_shop.myorders.com Server 6: anna_box.myorders.com
  • “REST” API V3 • Step 1: POST /login (user_name, password) – Session created, with User_Type, User_Domain returned • Step 2: – Free accounts: GET /{user_name}/orders – Pro accounts: GET pro.myorders.com/{user_name}/orders – VIP accounts: GET {user_domain}.myorders.com/orders • Step 3: – Free accounts: GET /{user_name}/orders/{order_id} – Pro accounts: GET pro.myorders.com/{user_name}orders/{order_id} – VIP accounts: GET {user_domain}.myorders.com/orders/{order_id} • URI cooking rules: – User_name retrieved from client-side input – User_type received from server, “free” or “pro” or “vip” – User_domain received from server, maybe null – List_URI = user_domain ? user_domain + ‘.myorders.com/orders’ : (user_type == ‘pro’ ? ‘pro.myorders.com/’ : ‘/’) + user_name + ‘/orders’ – Order_URI = List_URI + order_list[n].order.id • Seems not that simple for client anymore …
  • Again, what about old apps? • “We’ll support old client apps, as usual…” • Modify Orders servlet logic again: – Retrieve domain & user_name from request URI – If domain name is “Pro”  API V2/V3 • Use DB on pro.myorders.com – If domain name is not “Pro”  API V3 • Use DB on {domain_name}.myorders.com – If NOTHING is provided  API V1.0 • Retrieve user_name from session first, then • Then lookup Users table to get user_type, user_domain – If user_type is “Free”, use DB on myorders.com – If user_type is “Pro”, use DB on pro.myorders.com – If user_type is “VIP”, use DB on {user_domain}.myorders.com – If only user_name is provided  API V1.1 • Likewise, lookup Users table to determine which DB to use • In the end, API V1/V1.1/V2 still works fine, sadly …
  • Things Can Get Even More Complicated More requirements, more offerings, more functions, more features, more rules, clusters, load-balancers, data partitions, backups …
  • So Will Servlet Logic … And maintenance, logging, testing, trouble-shooting …
  • And Client App Implementation Cost
  • So What’s Wrong in the First Place?
  • “REST” API V1 • Step 1: POST /login (user_name, password) – Session created • Step 2: GET /orders – Get Order List, with user id implicitly provided in session – Should NOT let client assume the URI, if potential changes expected • Step 3: GET /orders/{order_id} – Get specific Order data through cooked URI – Should NOT let client assume the URI pattern , if potential changes expected • More assumptions allowed = More tightly coupling • Simple effort for one-time client implementation  possibly huge, on-going & ever-increasing liability for the server
  • A True REST API V0.1 Instead • Step 1: POST /login (user_name, password) – Session created, user related resource descriptions returned – User_Data: { name: “tom”, order_list_uri: “/tom/orders” } • Step 2: GET {User_Data.order_list_uri} – Retrieve order list data, sample data: – Order_List = [ order: {id:123, uri:“/tom/orders/123”} … ] • Step 3: GET {Order_List[n].order.uri} – Retrieve specific Order data through given URI
  • Same API Works across Various Account Types … Free Pro VIP POST /login POST /login POST /login User_Data: { User_Data: { User_Data: { name: “tom”, name: “jerry”, name: “susan”, order_list_uri: order_list_uri: order_list_uri: “/tom/orders” “pro.myorders.com/jerry/orders” “susan_test.myorders.com/orders” } } } GET /tom/orders GET pro.myorders.com/jerry/orders GET susan_test.myorders.com/orders Order_List = [ Order_List = [ Order_List = [ order: { order: { order: { id:123, id:456, id:789, uri:“/tom/orders/123” uri:“suasan_test.myorders.com/orders/78 } uri:“pro.myorders.com/jerry/orders/456”} 9”} ] ] ] GET /tom/orders/123 GET GET pro.myorders.com/jerry/orders/456 suasan_test.myorders.com/orders/7 89
  • … and Adaptable to Various Situations • Tom just upgrade from Free account to Pro, with bulk data migration scheduled later … And Tom can continue work across DBs Order_List = [ order:{ id:123, uri: ‘/tom/orders/123’ }, Data from different DBs mixed order:{ id:456, uri: ‘pro.myorders.com/tom/orders/456’ } ] • Pro.myorders.com is down for maintenance, and Pro_1 is up as backup … And users will hardly notice the change Order_List = [ order:{ id:123, uri: ‘pro_1.myorders.com/tom/orders/123’ } order:{ id:456, uri: ‘pro_1.myorders.com/tom/orders/456’ } ]
  • Put It Visually Imagine a Parking Lot
  • Different Zones for Different Parkers Free parkers Pro parkers VIP parker VIP parker VIP parker VIP parker VIP parker VIP parker VIP parker VIP parker Gate
  • A once-free-now-VIP Parker who cannot get rid of old habits … “Sir, your lot is in the VIP zone around the corner…” Pro parkers “!!!!...” “Since you’re VIP customer, we’ll redirect your car there for free …” Free parkers VIP parker VIP parker VIP parker VIP parker VIP parker VIP parker VIP parker VIP parker Gate
  • “REST” API without HATEOAS Be prepared to repeat this mess each and every day Pro parkers Free parkers VIP parker VIP parker VIP parker VIP parker VIP parker VIP parker VIP parker VIP parker Gate
  • A HATEOAS API Scenario Instructions to each customer each time Free parkers Pro parkers VIP parker VIP parker VIP parker VIP parker VIP parker VIP parker VIP parker UNDER CONSTRUCTION “Sir, your lot is being repaired, fortunately we’ve allocated a new one for you, here’s the route …” “I see, thanks a lot ” Gate
  • RPC vs. HATEOAS Not necessarily future-proof but more efficient for now Free Gate Pro Gate Free parkers Pro parkers VIP parker VIP parker VIP parker VIP parker VIP parker VIP parker VIP parker VIP parker VIP Gate Old Gate Blocked Old client: “!@#$$#%^&^%!!!!”
  • Take Away • HATEOAS is essential, for – APIs as well as internal organization of complex systems that may evolve over time – In order to minimize maintenance cost and support old client apps • However, mind that – Loose coupling  less efficiency • So, if you’re 100% sure something will never change, e.g., /login as login URL, just let everyone assume it forever