1
Web APIs that Developers Love Kai Spichale @kspichale 2
Communication Decoupling from among developers implementation Client API Implementation Integration & reuse Operations with input and output 3
Developer Perspective ? ► APIs are intended for developers ► Use standard HTTP ► Document precisely & with examples 4
URI Design for Web APIs 5
Unique identifier of resources /answers/42 Could be cryptic /xc487xdjfngx71dd Client should not create links by himself Server provides links 6
Hypermedia links (HATEOAS) "links": [ { "href": "https://api.sandbox.paypal.com/v1/ payments/payment/PAY-2XR80EBWOSA", "rel": "self", "method": "GET" } ] 7
Should You Care about URI Design? No! REST does not require proper URI design Yes! Many so-called „REST“ APIs ignore hypermedia Readable URIs help developers to understand the API 8
Advantages of URI Design Readable https://api.github.com/users/mojombo/followers Hackable https://api.github.com/users https://api.github.com/users/kspichale Stable No broken links HTTP 301 «Moved Permanently» 9
Best Practices for URIs Short Easier to read Abbreviations can be counterproductive Consistent Patterns, names, case Plural nouns Strict lowercase Hyphen 10
Don’t Be Dogmatic – Pseudo File Endings File of a web app http://www.ics.uci.edu/~fielding/pubs/ ↵ dissertation/top.htm Resource of a Web API http://userstream.twitter.com/1.1/user.json (There is no content negotiation anyway.) 11
Don’t Be Dogmatic – Trailing Slashes /foo/bar Unix file /foo/bar/ Unix directory Be consistent: Avoid trailing /books/ slahses /books/42 /books/42/chapters /books/42/chapters/1 12
Don’t Be Dogmatic – Trailing Slashes Directory resource / Provides navigation links to begin the interaction with the API Typical mistake http://api.example.com/ vs. http://api.example.com 13
1:* Relations Hierarchical URI pattern :collection/:item/:sub-collection/:sub-item 1 * Group Person POST /groups/g1/persons GET /groups/g1/persons/p1 DELETE /groups/g1/persons/p1 14
n:m Relations * * Group Person GET /groups/g1 { Caching? "id": "p1", Which one is "persons": ["p1", "p2"] leading? } GET /persons/p1 { "id": "g1", "groups": ["g1", "g2"] } 15
n:m Relations * * with Subscriptions Group Person POST /memberships { Membership "person_id": "p1", "group_id": "g1", Caching! } Atomicity! GET /membership/mem-p1-g1 Additional DELETE /membership/mem-p1-g1 properties! 16
Best Practices for IDs – Prefixes Makes URI more readable vs. /agents/agent007 /agents/007 /persons/83j9x vs. /persons/per-83j9x https://jira.spring.io/browse/DATAREST-516 17
Best Practices for IDs – Sequential IDs https://jira.spring.io/browse/DATAREST-516 Guessable (advantage): There must be a ticket 515 Hackable (advantage): Quick navigation to another ticket 18
Best Practices for IDs – Sequential IDs http://api.super-kewl-app.com/users/u-314 Guessable (disadvantage): There must be 313 other users Hackable (disadvantage): Potential security issue No 1:1 relation between database key and public ID 19
Best Practices for IDs – Slug IDs http://stackoverflow.com/questions/47427/why ↵ -do-some-websites-add-slugs-to-the-end-of-urls Natural key Created by URL encoding Search Engine Optimization Read content in link before clicking on it 20
Partial Responses 21
Partial Responses – Paging Product 10 Product 09 GET /products?page=1&count=5 ↵ Product 08 Product 07 &sort-by=id&order=desc Product 06 Product 05 Product 04 GET /products?page=2&count=5 ↵ Product 03 Product 02 &sort-by=id&order=desc Product 01 22
Partial Responses – Web Linking (RFC5899) "links": [ { "href": "http://api.example.com/products?page=1&count=5", "rel": [ "previous“ ], "method": "GET" }, { "href": "http://api.example.com/products?page=2&count=5", "rel": [ "self" ], "method": "GET" }, { "href": "http://api.example.com/products?page=3&count=5", "rel": [ "next" ], "method": "GET" } ] 23
Partial Responses – Web Linking (RFC5899) "links": [ { "href": "http://api.example.com/products?page=1&count=5", "rel": [ “first“ ], "method": "GET" }, { "href": "http://api.example.com/products?page=23&count=5", "rel": [ “last" ], "method": "GET" } ] 24
Partial Responses – Streams of Data Message 10 Message 12 Message 09 Message 11 page=1 Message 08 Message 10 count=5 Message 07 Message 09 processed Message 06 Message 08 Message 05 Message 07 Message 04 Message 06 Message 03 Message 05 Message 02 Message 04 Message 01 Message 03 Message 02 Message 01 25
Partial Responses – Streams of Data Message 12 Message 12 Message 11 Message 11 Message 10 Message 10 Message 09 Message 09 Message 08 Message 08 Message 07 Message 07 Message 06 Message 06 page=2 Message 05 Message 05 count=5 Message 04 Message 04 page=2 Message 03 Message 03 count=5 Message 02 Message 02 Message 01 Message 01 26
Partial Responses – Cursoring Message 10 Message 12 Message 09 Message 11 count=5 Message 08 Message 10 Message 07 Message 09 processed Message 06 Message 08 Message 05 Message 07 Message 04 Message 06 Message 03 Message 05 Message 02 Message 04 count=5 Message 01 Message 03 max_id=5 Message 02 Message 01 27
Partial Responses – Cursoring Message 18 Message 18 Message 17 Message 17 count=5 processed Message 16 Message 16 since_id=10 Message 15 Message 15 Message 14 Message 14 count=5 Message 13 Message 13 since_id=10 Message 12 Message 12 Message 11 Message 11 max_id=13 Message 10 Message 10 ... ... processed processed Message 02 Message 02 Message 01 Message 01 28
Siren – Embedding vs. Linking { _links": { "self": { "href": "/books/42" }, "author": { "href": "/people/douglas-adams"} } } Client may resolve links to sub-entities 29
Siren – Embedding vs. Linking { "_links": { "self": { "href": "/books/42" }, "author": { "href”: "/people/douglas-adams”} }, "_embedded": { "author": { "_links": { "self”: { "href": "/people/douglas-adams” } }, "name": "Douglas Noel Adams", "born": "March 11, 1952”, "died": "May 11, 2001" } } } 30
GraphQL – A Data-Fetching API A GraphQL query is sent as a string to a server to define the shape of the returned representation Developed by Facebook http://graphql.org Strongly typed 31
GraphQL – A Data-Fetching API { author(id:douglas-adams) { name } } Server can return exactly what a client asks for and no more: { "author": { "name": "Douglas Noel Adams" } } 32
GraphQL Selections Fields Complex Data Arguments { { { id releaseDate { book(id:42) { firstname month id lastname year title } } coverPic(size:100) } } } 33
GraphQL Selections Using the spread operator (...) and fragments fragment bookFields on Book { id title coverPic(size:100) } query bookQuery { book(id:42) { ...bookFields } } 34
GraphQL Mutations Mutation { likeBook(bookId: 42) { book { name likeCount } } } 35
API Changes GET /books/42 Response: { "id": 42, "title": "The Hitchhiker's Guide to ↵ the Galaxy", "price": "7.95" } 36
API Changes GET /books/42 Response: { "id": 42, "title": "The Hitchhiker's Guide to ↵ the Galaxy", "bruttoPrice": "7,95", "nettoPrice": "6,12" } 37
API Changes GET /books/42 Response: { "id": 42, "title": "The Hitchhiker's Guide to ↵ the Galaxy", "price": "7.95“, "bruttoPrice": "7,95", "nettoPrice": "6,12" } 38
GraphQL Introspection API GET /books/42 query BookIntrospection { __type(name: "Book") { name fields(includeDeprecated: true) { name isDeprecated deprecationReason } } } 39
GraphQL Introspection API { "data": { "__type": { "name": "Book", "fields": [ { "name": "id", "isDeprecated": false, "deprecationReason": null }, { "name": "price", "isDeprecated": true, "deprecationReason”: "Use ‘bruttoPrice’ instead." }, 40
Recommend
More recommend