Rest in practice

46
REST in practice How to design flexible and discoverable interfaces using HAL and Web API Ian Brennan, Application Architect, ezetop

description

How to design flexible and discoverable interfaces using HAL and Web API

Transcript of Rest in practice

Page 1: Rest in practice

REST in practice

How to design flexible and discoverable interfaces using HAL

and Web API

Ian Brennan, Application Architect, ezetop

Page 2: Rest in practice

ezetop

• Market leader in international and online top up– Wholesale, retail, consumer– Integrated, online, mobile apps, responsive

• 8 years building APIs– We have over 10 different APIs that allow you to top up a

mobile phone

• Challenge– Maintain compatibility with existing business– Implement new features– Move, divide, coalesce and transform existing services

Page 3: Rest in practice

Core Principles of REST based APIs

• Navigable– Your client works the way that a browser does– Ideally, the client only constructs a single URL

• Resource Driven– This is a “document driven architecture” rather than a

“service oriented architecture”

• HTTP-Centric– GET, PUT, POST, DELETE– Cache headers, eTags, redirects, locations

HATEOASHypertext as the engine of application state

Page 4: Rest in practice

REST ≠ RPC (even with JSON!)

REST• Client follows the

relationship between resources

• Client uses a URI template to build a query.

• Server delivers linking information between resources

• Our contract is resource types, and relationship types.

RPC• Client knows the URL

of each method• Client constructs query

string parameter to build a query.

• Client copies data between many models

• Our contract is endpoint URIs, request types, and reply types.

Page 5: Rest in practice

Some signs you aren't RESTful

• Client code Builds URLs• Client code constructs query strings• Most of your methods use POST• A proliferation of “request” and “response”

classes• A proliferation of model classes• Deeply nested models

Page 6: Rest in practice

Why bother?

• Flexible– Easy to extend– Easy to version– Built in service locator– Server side control of client side behaviour

• Easy to cache– Server can control and drive caching behaviour– Standard, well understood caching model

Page 7: Rest in practice

Designing a REST interface

• “Resource first” - Similar in nature to an ERD

Page 8: Rest in practice

HAL – Hypertext Application Language

• JSON based– Resources– Links– Embeds

http://tools.ietf.org/html/draft-kelly-json-hal

Page 9: Rest in practice

A Car, HAL style

{ "wheels": 4, "doors": 5, "_links": { "self": { "href": "http://example.com/car/1234-535" }}

Page 10: Rest in practice

Restrictions of HAL

• There is always a single resource delivered in the body of a single request– You can use embeds to send related resources

• All resources must have a “self” relationship– If it doesn’t have a URI, it isn’t a resource!

Page 11: Rest in practice

More useful relationships

{ "wheels": 4, "doors": 5, "_links": { "self": { "href": "http://example.com/car/1234-535" }, "journey-add": { "href": "http://example.com/journey?car=1234-535" }, "owner-query": { "href": "http://example.com/owner/someone" },

“change-wheel": { "href": "http://example.com/wheels/change?wheel={wheelNumber}" }}

Page 12: Rest in practice

Two Cars, HAL style

{ "count": 2, "_links": { "self": { "href": "http://example.com/car?owner=someone" } }, "_embedded": { "item": [ { "wheels": 4, "doors": 5, "_links": { "self": { "href": "http://example.com/car/1234-535" } } }, { "wheels": 4, "doors": 5, "_links": { "self": { "href": "http://example.com/car/1234-5356" } } }]}}

Page 13: Rest in practice

The HAL Browser

• JQuery based • Browse any

HAL API

Page 14: Rest in practice

HAL Alternatives

• AtomPub/Collection+JSON– Oldest hypermedia implementation– Strong query support– Strong collection support

• Siren– Invests more in describing relationships– More discoverable as a result– Strong validation semantics

• So why HAL?– Simple, unobtrusive– Wide library support– Human readable

Page 15: Rest in practice

What’s in a link?

Better living through good relationships

Page 16: Rest in practice

Standard Relationships

• self • item • edit • next• last• profile• curie

List is quite arbitrary – there is no:• delete• add

Mostly because these are done through verbs (but then why have edit?)

http://www.iana.org/assignments/link-relations/link-relations.xhtml

Page 17: Rest in practice

Custom Relationships

• Non-IANA relationships should be URIs• Curies make this readable

"_links": { "self": { "href": "/api/provider/Auris" }, "curies": { "href": "http://schema.ezetop.com/ild/{rel}", "name": "ild", "templated": true }, "ild:product-query": { "href": "/api/product?provider=Auris" } }

Page 18: Rest in practice

You reach everything by navigating relationships

Car

WheelWheel Wheel

TyreTyreTyre

veh:wheelveh:wheelveh:wheel

veh:tyre veh:tyreveh:tyre

client .Home("/car").Follow("veh:wheel").Follow("veh:tyre");

7 network requests!

Highly cacheable

Page 19: Rest in practice

Car

We can optimize using embeds...

WheelWheel Wheel

TyreTyreTyre

veh:wheelveh:wheelveh:wheel

veh:tyre veh:tyreveh:tyre

client .Home("/car").Follow("veh:wheel").Follow("veh:tyre");

Caching - Moderate

4 Network Requests

Page 20: Rest in practice

Car

...as much as we like

WheelWheel Wheel

TyreTyreTyre

veh:wheelveh:wheelveh:wheel

veh:tyre veh:tyreveh:tyre

client .Home("/car").Follow("veh:wheel").Follow("veh:tyre");

Caching - Awful

1 Network Request

Page 21: Rest in practice

Hypertext Cache Pattern

• A well written client does not care if it’s a link or an embed

• Treats embeds as if they were GET requests• Optimization becomes a server side concern

client .Home("/car").Follow("veh:wheel").Follow("veh:tyre");

Page 22: Rest in practice

One to many?

Link directly• Works fine if we know there are

strictly bounded quantities

Intermediate grouping resource

• Better for queries• Can support paging

• Page resource open to extension

Car

WheelWheel Wheel

veh:wheelPage<Product>

ProductProduct Product

item

Catalog

shop:product-query

client .Home("/car").Follow("veh:wheel");

client .Home("/catalog").Follow(“shop:product-query").Follow(“item”);

Page 23: Rest in practice

Linked items or embedded items?

• In ezetop our framework embeds collection items within pages

• We sometimes link them too

Page<Product>

Product Product Product

item

Page<Product>

ProductProduct Product

item

Page 24: Rest in practice

URI Templates

• RFC6570– Uri that allows value substitution– Simple values, but also lists and dictionaries– Not restricted to query strings

• Implementations exist in most languages• Can be used in HAL LinkRelations

– Link has "templated": true– Loosely coupled URI construction

• Lightweight request types– Use sparingly!

http://www.com/car?p={pageNumber}&s={pageSize}

Page 25: Rest in practice

Caching

• HATEOS interfaces tend to be very chatty– Caching is essential

• HTTP caching mechanisms– Cache-Control headers– eTags

• Server controls the client cache – The server never caches content.

client .Home(“http://ildserver") .Follow(“item”) .Follow(“ild:tenantproduct-by-tenant”) .Follow(“ild:account-by-consumerref”) .Follow(“ild:callernumber-query”) .Follow(“item”)

client .Home(“http://ildserver") .Follow(“item”) .Follow(“ild:tenantproduct-by-tenant”) .Follow(“ild:account-by-consumerref”) .Follow(“ild:callernumber-query”) .Follow(“item”)

Page 26: Rest in practice

CacheCow.Client

• Fully functional cache for .NET apps that use HttpClient– It decorates HttpClientHandler with caching semantics– Simple cache-control headers– Support for eTags also– Set it up through your IOC framework of choice

• It has a pluggable store for cached resources– SQL Server, Memcached and you can build your own

https://github.com/aliostad/CacheCow

Page 27: Rest in practice

Cache Store

Server

Simple Caching

• Time Based– Cache-Control headers– Just like a browser cache

Resource<Product>

Client

Resource<Product>

GET

200 OKMaxAge: 10m

Page 28: Rest in practice

What’s an eTag?

• An eTag in an HTTP response defines a version of a delivered resource

• Browser automatically asks if that version still valid (If-None-Match)

• Server says yes (304 Not Modified)• Client uses it’s cached version• Server does not have to re-generate resource• Built in support in all browsers

Page 29: Rest in practice

GETeTag: xzy

Cache Store

Server

ETag Caching with CacheCow.Server

• Content based• eTag Hash generated

from resource and URI• Explicit clearing

supported• Entity Tag Store

– Pluggable, but standard stores exist

• Challenging to configure

Resource<Product>

Client

Resource<Product>

Entity Tag Store

Product tag

GET

200 OKETag: xyz

304 Not-Modified

Page 30: Rest in practice

Consuming REST APIs

• Your client needs to be able to– Navigate relationships– Parse URI templates– Understand embeds– Support caching (including eTags)– Implement the Hypertext cache pattern

• HAL is easy – navigation is hard

• Is HAL suitable for a public API?– Amazon do it, among others.

Page 31: Rest in practice

REST in ezetop

• We have started to adopt HATAEOS – August 2013

• All new services are fully RESTful• Older services are

– RPC-JSON– WCF

• Gradual transition to– Hypermedia API– Flat structure

• Toolset– Web API 4; CacheCow; Tavis.UrlTemplates;

Page 32: Rest in practice

What we like

Easy to write services Flexible (not WCF!) Stateless Platform independent Standard “API First”

Page 33: Rest in practice

What we don’t

Learning curveToo much boilerplate in server codeContract is not fully self documentingChattinessDealing with collections of resources

Page 34: Rest in practice

“I wouldn’t start from here...”

• What is the correct home page for a particular API?– /api/ with all versions available?– /api/1

• What should I hard code?

Page 35: Rest in practice

“Is that the best way to get there?”

• Choosing the best navigation path is non-obvious– Iterate early on the design

• Avoid client repetition• Avoid backtracking

– Where possible!

Page 36: Rest in practice

“Do you even know the way yourself?”

• It’s tough to generate fully qualified URIs– Dev/staging/production scenarios– “Sibling” APIs

• Location pattern built in, but is not free.• Strong URI-generation strategies are vital

Page 37: Rest in practice

Naming is hard, part 1001

• HAL is non-prescriptive on relationships– Relationship name is relatively anaemic– Siren is better here

• A good naming convention can help a lot– Your relationships define your contract

Page 38: Rest in practice

“Why won’t you do what you’re told?”

• Clients can simply ignore RESTful semantics– Parse URIs and use them to build a new query string– Rebuild links from scratch instead of accepting them

from the serving application– Use string-replace instead of templated URIs

• Randomly mutate URIs? • Provide client libraries?

Page 39: Rest in practice

“Just let me GET that for you...”

• Proxy patterns are bad in REST– We can’t cache Product!

• Need to avoid this anti-pattern

Web Site Ordering System

Catalog SystemREST client IldVersion

TenantProduct

Product

Product

ProductCode

ProductLimit

ProductColor

Page 40: Rest in practice

The problem with layer cakes

Topup Payments

Mobile App

Gateway

Mobile API

Ordering API

Cheap Calls

Gateway

Gateway

Page 41: Rest in practice

The alternative is to spread it around!

Topup

Payments

Mobile App

Gateway

Ordering API

Cheap Calls

Gateway

Gateway

Page 42: Rest in practice

Library Support

• Libraries are available for both HAL and URI templates in most languages

• Ruby, Python, Javascript, PHP, C#, Java, Scala, Objective-C, Eiffel, Clojure, Go

• Quality varies• Navigation semantics will likely not be covered• Hypertext cache pattern will likely not be covered

https://github.com/mikekelly/hal_specification/wiki/Librarieshttps://code.google.com/p/uri-templates/wiki/Implementations

Page 43: Rest in practice

HAL in Web API

• Not a 100% natural fit – but working for us

• You need to return Hal resources instead of “raw” models– Inheritance or composition both work– We use composition (with generics)

• There is room for improvement– Consider alternatives in green field projects

Page 44: Rest in practice

Final thoughts

• REST is 100% pull/poll• Emphasis is on flexibility• Good caching strategies are vital• Encourages a “flat service” structure

– but that requires good security

• Works well for us as an enterprise architecture– however, it relies on good client code

Page 45: Rest in practice

Further Research

• REST– “REST in Practice” book– http://chimera.labs.oreilly.com/books/1234000001708/in

dex.html

• URI Templates– http://tools.ietf.org/html/rfc6570– https://code.google.com/p/uri-templates/wiki/Implement

ations– https://github.com/tavis-software/UriTemplates

• HAL– http://stateless.co/hal_specification.html– http://tools.ietf.org/html/draft-kelly-json-hal

• CacheCow– https://github.com/aliostad/CacheCow

Page 46: Rest in practice

Thanks for listening!

@ianbruhttp://ie.linkedin.com/in/ianbrennan/

http://www.slideshare.net/IanBrennan1