designing a beautiful rest json api
TRANSCRIPT
![Page 1: Designing a beautiful REST json api](https://reader034.vdocuments.site/reader034/viewer/2022052509/55cc7d9dbb61ebff138b467d/html5/thumbnails/1.jpg)
Beautiful REST+JSON APIs
Les Hazlewood @lhazlewood Apache Shiro Project Chair
Expert Group Member, JEE Application Security CTO, Stormpath, stormpath.com
![Page 2: Designing a beautiful REST json api](https://reader034.vdocuments.site/reader034/viewer/2022052509/55cc7d9dbb61ebff138b467d/html5/thumbnails/2.jpg)
@lhazlewood
.com • User Management API for Developers
• Password security
• Authentication and Authorization
• LDAP & Active Directory Cloud Sync
• Instant-on, scalable, and highly available
• Free for developers
![Page 3: Designing a beautiful REST json api](https://reader034.vdocuments.site/reader034/viewer/2022052509/55cc7d9dbb61ebff138b467d/html5/thumbnails/3.jpg)
@lhazlewood
Outline • APIs, REST & JSON • REST Fundamentals • Design
Base URL Versioning Resource Format Return Values Content NegoEaEon References (Linking) PaginaEon Query Parameters AssociaEons
Errors IDs Method Overloading Resource Expansion ParEal Responses Caching & Etags Security MulE Tenancy Maintenance Batch OperaEons
![Page 4: Designing a beautiful REST json api](https://reader034.vdocuments.site/reader034/viewer/2022052509/55cc7d9dbb61ebff138b467d/html5/thumbnails/4.jpg)
@lhazlewood
APIs
• Applications • Developers • Pragmatism over Ideology • Adoption • Scale
![Page 5: Designing a beautiful REST json api](https://reader034.vdocuments.site/reader034/viewer/2022052509/55cc7d9dbb61ebff138b467d/html5/thumbnails/5.jpg)
@lhazlewood
Why REST?
• Scalability • Generality • Independence • Latency (Caching) • Security • Encapsulation
![Page 6: Designing a beautiful REST json api](https://reader034.vdocuments.site/reader034/viewer/2022052509/55cc7d9dbb61ebff138b467d/html5/thumbnails/6.jpg)
@lhazlewood
Why JSON?
• Ubiquity • Simplicity • Readability • Scalability • Flexibility
![Page 7: Designing a beautiful REST json api](https://reader034.vdocuments.site/reader034/viewer/2022052509/55cc7d9dbb61ebff138b467d/html5/thumbnails/7.jpg)
@lhazlewood
HATEOAS
• Hypermedia
• As
• The
• Engine
• Of
• Application
• State
![Page 8: Designing a beautiful REST json api](https://reader034.vdocuments.site/reader034/viewer/2022052509/55cc7d9dbb61ebff138b467d/html5/thumbnails/8.jpg)
@lhazlewood
REST Is Easy
![Page 9: Designing a beautiful REST json api](https://reader034.vdocuments.site/reader034/viewer/2022052509/55cc7d9dbb61ebff138b467d/html5/thumbnails/9.jpg)
@lhazlewood
REST Is *&@#$! Hard
(for providers)
![Page 10: Designing a beautiful REST json api](https://reader034.vdocuments.site/reader034/viewer/2022052509/55cc7d9dbb61ebff138b467d/html5/thumbnails/10.jpg)
@lhazlewood
REST can be easy
(if you follow some guidelines)
![Page 11: Designing a beautiful REST json api](https://reader034.vdocuments.site/reader034/viewer/2022052509/55cc7d9dbb61ebff138b467d/html5/thumbnails/11.jpg)
Example Domain: Stormpath
• Applications • Directories • Accounts • Groups • Associations • Workflows
![Page 12: Designing a beautiful REST json api](https://reader034.vdocuments.site/reader034/viewer/2022052509/55cc7d9dbb61ebff138b467d/html5/thumbnails/12.jpg)
@lhazlewood
Fundamentals
![Page 13: Designing a beautiful REST json api](https://reader034.vdocuments.site/reader034/viewer/2022052509/55cc7d9dbb61ebff138b467d/html5/thumbnails/13.jpg)
@lhazlewood
Resources
Nouns, not Verbs Coarse Grained, not Fine Grained Architectural style for use-case scalability
![Page 14: Designing a beautiful REST json api](https://reader034.vdocuments.site/reader034/viewer/2022052509/55cc7d9dbb61ebff138b467d/html5/thumbnails/14.jpg)
@lhazlewood
What If? /getAccount /createDirectory /updateGroup /verifyAccountEmailAddress
![Page 15: Designing a beautiful REST json api](https://reader034.vdocuments.site/reader034/viewer/2022052509/55cc7d9dbb61ebff138b467d/html5/thumbnails/15.jpg)
@lhazlewood
What If? /getAccount /getAllAccounts /searchAccounts /createDirectory /createLdapDirectory /updateGroup /updateGroupName /findGroupsByDirectory /searchGroupsByName /verifyAccountEmailAddress /verifyAccountEmailAddressByToken … Smells like bad RPC. DON’T DO THIS.
![Page 16: Designing a beautiful REST json api](https://reader034.vdocuments.site/reader034/viewer/2022052509/55cc7d9dbb61ebff138b467d/html5/thumbnails/16.jpg)
@lhazlewood
Keep It Simple
![Page 17: Designing a beautiful REST json api](https://reader034.vdocuments.site/reader034/viewer/2022052509/55cc7d9dbb61ebff138b467d/html5/thumbnails/17.jpg)
@lhazlewood
The Answer
Fundamentally two types of resources: Collection Resource Instance Resource
![Page 18: Designing a beautiful REST json api](https://reader034.vdocuments.site/reader034/viewer/2022052509/55cc7d9dbb61ebff138b467d/html5/thumbnails/18.jpg)
@lhazlewood
Collection Resource
/applications
![Page 19: Designing a beautiful REST json api](https://reader034.vdocuments.site/reader034/viewer/2022052509/55cc7d9dbb61ebff138b467d/html5/thumbnails/19.jpg)
@lhazlewood
Instance Resource
/applications/a1b2c3
![Page 20: Designing a beautiful REST json api](https://reader034.vdocuments.site/reader034/viewer/2022052509/55cc7d9dbb61ebff138b467d/html5/thumbnails/20.jpg)
@lhazlewood
Behavior • GET • PUT • POST • DELETE • HEAD
![Page 21: Designing a beautiful REST json api](https://reader034.vdocuments.site/reader034/viewer/2022052509/55cc7d9dbb61ebff138b467d/html5/thumbnails/21.jpg)
@lhazlewood
Behavior POST, GET, PUT, DELETE
≠ 1:1
Create, Read, Update, Delete
![Page 22: Designing a beautiful REST json api](https://reader034.vdocuments.site/reader034/viewer/2022052509/55cc7d9dbb61ebff138b467d/html5/thumbnails/22.jpg)
@lhazlewood
Behavior As you would expect: GET = Read DELETE = Delete HEAD = Headers, no Body
![Page 23: Designing a beautiful REST json api](https://reader034.vdocuments.site/reader034/viewer/2022052509/55cc7d9dbb61ebff138b467d/html5/thumbnails/23.jpg)
@lhazlewood
Behavior Not so obvious: PUT and POST can both be used for Create and Update
![Page 24: Designing a beautiful REST json api](https://reader034.vdocuments.site/reader034/viewer/2022052509/55cc7d9dbb61ebff138b467d/html5/thumbnails/24.jpg)
@lhazlewood
PUT for Create IdenEfier is known by the client: PUT /applications/clientSpecifiedId
{
…
}
![Page 25: Designing a beautiful REST json api](https://reader034.vdocuments.site/reader034/viewer/2022052509/55cc7d9dbb61ebff138b467d/html5/thumbnails/25.jpg)
@lhazlewood
PUT for Update Full Replacement PUT /applications/existingId
{
“name”: “Best App Ever”,
“description”: “Awesomeness”
}
![Page 26: Designing a beautiful REST json api](https://reader034.vdocuments.site/reader034/viewer/2022052509/55cc7d9dbb61ebff138b467d/html5/thumbnails/26.jpg)
@lhazlewood
PUT
Idempotent
![Page 27: Designing a beautiful REST json api](https://reader034.vdocuments.site/reader034/viewer/2022052509/55cc7d9dbb61ebff138b467d/html5/thumbnails/27.jpg)
@lhazlewood
POST as Create On a parent resource POST /applications { “name”: “Best App Ever” } Response: 201 Created Location: https://api.stormpath.com/applications/a1b2c3
![Page 28: Designing a beautiful REST json api](https://reader034.vdocuments.site/reader034/viewer/2022052509/55cc7d9dbb61ebff138b467d/html5/thumbnails/28.jpg)
@lhazlewood
POST as Update On instance resource POST /applications/a1b2c3 { “name”: “Best App Ever. Srsly.” } Response: 200 OK
![Page 29: Designing a beautiful REST json api](https://reader034.vdocuments.site/reader034/viewer/2022052509/55cc7d9dbb61ebff138b467d/html5/thumbnails/29.jpg)
@lhazlewood
POST
NOT Idempotent
![Page 30: Designing a beautiful REST json api](https://reader034.vdocuments.site/reader034/viewer/2022052509/55cc7d9dbb61ebff138b467d/html5/thumbnails/30.jpg)
@lhazlewood
Media Types
• Format SpecificaEon + Parsing Rules • Request: Accept header • Response: Content-Type header
• application/json • application/foo+json • application/foo+json;application • …
![Page 31: Designing a beautiful REST json api](https://reader034.vdocuments.site/reader034/viewer/2022052509/55cc7d9dbb61ebff138b467d/html5/thumbnails/31.jpg)
@lhazlewood
Design Time!
![Page 32: Designing a beautiful REST json api](https://reader034.vdocuments.site/reader034/viewer/2022052509/55cc7d9dbb61ebff138b467d/html5/thumbnails/32.jpg)
@lhazlewood
Base URL
![Page 33: Designing a beautiful REST json api](https://reader034.vdocuments.site/reader034/viewer/2022052509/55cc7d9dbb61ebff138b467d/html5/thumbnails/33.jpg)
@lhazlewood
http(s)://foo.io vs
http://www.foo.com/dev/service/api/rest
![Page 34: Designing a beautiful REST json api](https://reader034.vdocuments.site/reader034/viewer/2022052509/55cc7d9dbb61ebff138b467d/html5/thumbnails/34.jpg)
@lhazlewood
http(s)://foo.io
Rest Client vs
Browser
![Page 35: Designing a beautiful REST json api](https://reader034.vdocuments.site/reader034/viewer/2022052509/55cc7d9dbb61ebff138b467d/html5/thumbnails/35.jpg)
@lhazlewood
Versioning
![Page 36: Designing a beautiful REST json api](https://reader034.vdocuments.site/reader034/viewer/2022052509/55cc7d9dbb61ebff138b467d/html5/thumbnails/36.jpg)
@lhazlewood
URL https://api.stormpath.com/v1 vs. Media-Type application/foo+json;application&v=2 application/foo2+json;application
![Page 37: Designing a beautiful REST json api](https://reader034.vdocuments.site/reader034/viewer/2022052509/55cc7d9dbb61ebff138b467d/html5/thumbnails/37.jpg)
@lhazlewood
Resource Format
![Page 38: Designing a beautiful REST json api](https://reader034.vdocuments.site/reader034/viewer/2022052509/55cc7d9dbb61ebff138b467d/html5/thumbnails/38.jpg)
@lhazlewood
Media Type
Content-Type: application/json When time allows: application/foo+json
application/foo2+json;bar=baz
…
![Page 39: Designing a beautiful REST json api](https://reader034.vdocuments.site/reader034/viewer/2022052509/55cc7d9dbb61ebff138b467d/html5/thumbnails/39.jpg)
@lhazlewood
Media Type Don’t go overboard! Media Type != Schema! Most only need 2 or 3 custom media types: • instance resource • collection resource
application/foo+json application/foo2+json;bar=baz …
![Page 40: Designing a beautiful REST json api](https://reader034.vdocuments.site/reader034/viewer/2022052509/55cc7d9dbb61ebff138b467d/html5/thumbnails/40.jpg)
@lhazlewood
camelCase ‘JS’ in ‘JSON’ = JavaScript myArray.forEach Not myArray.for_each account.givenName Not account.given_name Underscores for property/function names are unconventional for JS. Stay consistent.
![Page 41: Designing a beautiful REST json api](https://reader034.vdocuments.site/reader034/viewer/2022052509/55cc7d9dbb61ebff138b467d/html5/thumbnails/41.jpg)
@lhazlewood
Date/Time/Timestamp There’s already a standard. Use it: ISO 8601 Example: { …, “createdAt”: “2013-07-10T18:02:24.343Z”, ... } Use UTC!
![Page 42: Designing a beautiful REST json api](https://reader034.vdocuments.site/reader034/viewer/2022052509/55cc7d9dbb61ebff138b467d/html5/thumbnails/42.jpg)
@lhazlewood
createdAt / updatedAt
![Page 43: Designing a beautiful REST json api](https://reader034.vdocuments.site/reader034/viewer/2022052509/55cc7d9dbb61ebff138b467d/html5/thumbnails/43.jpg)
@lhazlewood
createdAt / updatedAt
Most people will want this at some point { …, “createdAt”: “2013-07-10T18:02:24.343Z”, “updatedAt”: “2014-09-29T07:02:48.761Z” } Use UTC!
![Page 44: Designing a beautiful REST json api](https://reader034.vdocuments.site/reader034/viewer/2022052509/55cc7d9dbb61ebff138b467d/html5/thumbnails/44.jpg)
@lhazlewood
Response Body
![Page 45: Designing a beautiful REST json api](https://reader034.vdocuments.site/reader034/viewer/2022052509/55cc7d9dbb61ebff138b467d/html5/thumbnails/45.jpg)
@lhazlewood
GET obvious
What about POST?
Return the representation in the response when feasible.
Add override (?_body=false) for control
![Page 46: Designing a beautiful REST json api](https://reader034.vdocuments.site/reader034/viewer/2022052509/55cc7d9dbb61ebff138b467d/html5/thumbnails/46.jpg)
@lhazlewood
Content Negotiation
![Page 47: Designing a beautiful REST json api](https://reader034.vdocuments.site/reader034/viewer/2022052509/55cc7d9dbb61ebff138b467d/html5/thumbnails/47.jpg)
@lhazlewood
Header
• Accept header • Header values comma delimited • q param determines precedence, defaults
to 1, then conventionally by list order
GET /applications/a1b2c3
Accept: application/json, text/plain;q=0.8
![Page 48: Designing a beautiful REST json api](https://reader034.vdocuments.site/reader034/viewer/2022052509/55cc7d9dbb61ebff138b467d/html5/thumbnails/48.jpg)
@lhazlewood
Resource Extension
/applications/a1b2c3.json /applications/a1b2c3.csv
…
ConvenEonally overrides Accept header
![Page 49: Designing a beautiful REST json api](https://reader034.vdocuments.site/reader034/viewer/2022052509/55cc7d9dbb61ebff138b467d/html5/thumbnails/49.jpg)
@lhazlewood
HREF
• Distributed Hypermedia is paramount!
• Every accessible Resource has a canonical unique URL
• Replaces IDs (IDs exist, but are opaque). • Critical for linking, as we’ll soon see
![Page 50: Designing a beautiful REST json api](https://reader034.vdocuments.site/reader034/viewer/2022052509/55cc7d9dbb61ebff138b467d/html5/thumbnails/50.jpg)
@lhazlewood
Instance w/ HREF (v1)
GET /accounts/x7y8z9 200 OK
{
“href”: “https://api.stormpath.com/v1/accounts/x7y8z9”,
“givenName”: “Tony”,
“surname”: “Stark”,
...
}
![Page 51: Designing a beautiful REST json api](https://reader034.vdocuments.site/reader034/viewer/2022052509/55cc7d9dbb61ebff138b467d/html5/thumbnails/51.jpg)
@lhazlewood
Resource References aka ‘Linking’
(v1)
![Page 52: Designing a beautiful REST json api](https://reader034.vdocuments.site/reader034/viewer/2022052509/55cc7d9dbb61ebff138b467d/html5/thumbnails/52.jpg)
@lhazlewood
• Hypermedia is paramount. • Linking is fundamental to scalability.
• Tricky in JSON • XML has it (XLink), JSON doesn’t • How do we do it?
![Page 53: Designing a beautiful REST json api](https://reader034.vdocuments.site/reader034/viewer/2022052509/55cc7d9dbb61ebff138b467d/html5/thumbnails/53.jpg)
@lhazlewood
Instance Reference (v1)
GET /accounts/x7y8z9 200 OK
{
“href”: “https://api.stormpath.com/v1/accounts/x7y8z9”,
“givenName”: “Tony”,
“surname”: “Stark”,
…,
“directory”: ???? }
![Page 54: Designing a beautiful REST json api](https://reader034.vdocuments.site/reader034/viewer/2022052509/55cc7d9dbb61ebff138b467d/html5/thumbnails/54.jpg)
@lhazlewood
Instance Reference (v1)
GET /accounts/x7y8z9 200 OK
{
“href”: “https://api.stormpath.com/v1/accounts/x7y8z9”,
“givenName”: “Tony”,
“surname”: “Stark”,
…,
“directory”: { “href”: “https://api.stormpath.com/v1/directories/g4h5i6”
}
}
![Page 55: Designing a beautiful REST json api](https://reader034.vdocuments.site/reader034/viewer/2022052509/55cc7d9dbb61ebff138b467d/html5/thumbnails/55.jpg)
@lhazlewood
Collection Reference (v1)
GET /accounts/x7y8z9 200 OK {
“href”: “https://api.stormpath.com/v1/accounts/x7y8z9”, “givenName”: “Tony”,
“surname”: “Stark”, …,
“groups”: { “href”: “https://api.stormpath.com/v1/accounts/x7y8z9/groups”
} }
![Page 56: Designing a beautiful REST json api](https://reader034.vdocuments.site/reader034/viewer/2022052509/55cc7d9dbb61ebff138b467d/html5/thumbnails/56.jpg)
@lhazlewood
Linking v2 (recommended)
![Page 57: Designing a beautiful REST json api](https://reader034.vdocuments.site/reader034/viewer/2022052509/55cc7d9dbb61ebff138b467d/html5/thumbnails/57.jpg)
@lhazlewood
Instance HREF (v2)
GET /accounts/x7y8z9 200 OK { “meta”: { “href”: “https://api.stormpath.com/v1/accounts/x7y8z9”, “mediaType”: “application/ion+json”, ... }, “givenName”: “Tony”,
“surname”: “Stark”, …
}
![Page 58: Designing a beautiful REST json api](https://reader034.vdocuments.site/reader034/viewer/2022052509/55cc7d9dbb61ebff138b467d/html5/thumbnails/58.jpg)
@lhazlewood
Instance Reference (v2) GET /accounts/x7y8z9 200 OK { “meta”: { ... }, “givenName”: “Tony”, “surname”: “Stark”, …, “directory”: { “meta”: { “href”: “https://api.stormpath.com/v1/directories/g4h5i6” “mediaType”: “application/ion+json” } } }
![Page 59: Designing a beautiful REST json api](https://reader034.vdocuments.site/reader034/viewer/2022052509/55cc7d9dbb61ebff138b467d/html5/thumbnails/59.jpg)
@lhazlewood
Collection Reference (v2) GET /accounts/x7y8z9 200 OK { “meta”: { ... }, “givenName”: “Tony”, “surname”: “Stark”, …, “groups”: { “meta”: { “href”: “https://api.stormpath.com/v1/accounts/x7y8z9/groups”, “mediaType”: “application/ion+json”, “rel”: [“collection”] } } }
![Page 60: Designing a beautiful REST json api](https://reader034.vdocuments.site/reader034/viewer/2022052509/55cc7d9dbb61ebff138b467d/html5/thumbnails/60.jpg)
@lhazlewood
Reference Expansion
(aka Entity Expansion, Link Expansion)
![Page 61: Designing a beautiful REST json api](https://reader034.vdocuments.site/reader034/viewer/2022052509/55cc7d9dbb61ebff138b467d/html5/thumbnails/61.jpg)
@lhazlewood
Account and its Directory?
![Page 62: Designing a beautiful REST json api](https://reader034.vdocuments.site/reader034/viewer/2022052509/55cc7d9dbb61ebff138b467d/html5/thumbnails/62.jpg)
@lhazlewood
GET /accounts/x7y8z9?expand=directory
200 OK
{
“meta”: {...},
“givenName”: “Tony”,
“surname”: “Stark”,
…,
“directory”: {
“meta”: { ... },
“name”: “Avengers”, “description”: “Hollywood’s hope for more $”, “createdAt”: “2012-07-01T14:22:18.029Z”, … }
}
![Page 63: Designing a beautiful REST json api](https://reader034.vdocuments.site/reader034/viewer/2022052509/55cc7d9dbb61ebff138b467d/html5/thumbnails/63.jpg)
@lhazlewood
Partial Representations
![Page 64: Designing a beautiful REST json api](https://reader034.vdocuments.site/reader034/viewer/2022052509/55cc7d9dbb61ebff138b467d/html5/thumbnails/64.jpg)
@lhazlewood
GET /accounts/x7y8z9?fields=givenName,surname,directory(name)
![Page 65: Designing a beautiful REST json api](https://reader034.vdocuments.site/reader034/viewer/2022052509/55cc7d9dbb61ebff138b467d/html5/thumbnails/65.jpg)
@lhazlewood
Collections!
![Page 66: Designing a beautiful REST json api](https://reader034.vdocuments.site/reader034/viewer/2022052509/55cc7d9dbb61ebff138b467d/html5/thumbnails/66.jpg)
@lhazlewood
Collections
• A first class resource ‘citizen’ • Own href / metadata • Own properties • Different from all other collections
![Page 67: Designing a beautiful REST json api](https://reader034.vdocuments.site/reader034/viewer/2022052509/55cc7d9dbb61ebff138b467d/html5/thumbnails/67.jpg)
@lhazlewood
GET /accounts/x7y8z9/groups
200 OK { “meta”: { ... }, “offset”: 0, “limit”: 25, “size”: 289, “first”: { “meta”:{“href”: “…/accounts/x7y8z9/groups?offset=0”}}, “previous”: null, “next”: { “meta”:{“href”: “…/accounts/x7y8z9/groups?offset=25”}}, “last”: { “meta”:{“href”: “…”}}, “items”: [ { “meta”: { “href”: “…”, ...} }, … ] }
![Page 68: Designing a beautiful REST json api](https://reader034.vdocuments.site/reader034/viewer/2022052509/55cc7d9dbb61ebff138b467d/html5/thumbnails/68.jpg)
@lhazlewood
Pagination
![Page 69: Designing a beautiful REST json api](https://reader034.vdocuments.site/reader034/viewer/2022052509/55cc7d9dbb61ebff138b467d/html5/thumbnails/69.jpg)
@lhazlewood
Collection Resource supports query params: • Offset • Limit …/applications?offset=50&limit=25
![Page 70: Designing a beautiful REST json api](https://reader034.vdocuments.site/reader034/viewer/2022052509/55cc7d9dbb61ebff138b467d/html5/thumbnails/70.jpg)
@lhazlewood
GET /accounts/x7y8z9/groups 200 OK { “meta”: { ... }, “offset”: 0, “limit”: 25, “first”: { “meta”:{“href”: “…/accounts/x7y8z9/groups?offset=0”}}, “previous”: null, “next”: { “meta”:{“href”: “…/accounts/x7y8z9/groups?offset=25”}}, “last”: { “meta”:{“href”: “…”}}, “items”: [ { “meta”: { “href”: “…”, ...} }, { “meta”: { “href”: “…”, ...} }, … ] }
![Page 71: Designing a beautiful REST json api](https://reader034.vdocuments.site/reader034/viewer/2022052509/55cc7d9dbb61ebff138b467d/html5/thumbnails/71.jpg)
@lhazlewood
Sorting
![Page 72: Designing a beautiful REST json api](https://reader034.vdocuments.site/reader034/viewer/2022052509/55cc7d9dbb61ebff138b467d/html5/thumbnails/72.jpg)
@lhazlewood
GET .../accounts? orderBy=surname,givenName%20desc
![Page 73: Designing a beautiful REST json api](https://reader034.vdocuments.site/reader034/viewer/2022052509/55cc7d9dbb61ebff138b467d/html5/thumbnails/73.jpg)
@lhazlewood
Search
![Page 74: Designing a beautiful REST json api](https://reader034.vdocuments.site/reader034/viewer/2022052509/55cc7d9dbb61ebff138b467d/html5/thumbnails/74.jpg)
@lhazlewood
“Find all accounts with a ‘company.com’ email address that can login to a particular
application”
![Page 75: Designing a beautiful REST json api](https://reader034.vdocuments.site/reader034/viewer/2022052509/55cc7d9dbb61ebff138b467d/html5/thumbnails/75.jpg)
@lhazlewood
GET /applications/x7y8z9/accounts? email=*company.com 200 OK { “meta”: { ... }, “offset”: 0, “limit”: 25, “first”: { “meta”:{“href”: “/applications/x7y8z9/accounts?email=*company.com&offset=0”}}, “previous”: null, “next”: { “meta”:{“href”: “/applications/x7y8z9/accounts?email=*company.com&offset=25”}}, “last”: { “meta”:{“href”: “…”}}, “items”: [ { “meta”: { “href”: “…”, ...} }, { “meta”: { “href”: “…”, ...} }, … ] }
![Page 76: Designing a beautiful REST json api](https://reader034.vdocuments.site/reader034/viewer/2022052509/55cc7d9dbb61ebff138b467d/html5/thumbnails/76.jpg)
@lhazlewood
Search cont’d
• Filter search .../accounts?q=some+value
• Attribute Search .../accounts?surname=Joe&email=*company.com
![Page 77: Designing a beautiful REST json api](https://reader034.vdocuments.site/reader034/viewer/2022052509/55cc7d9dbb61ebff138b467d/html5/thumbnails/77.jpg)
@lhazlewood
Search cont’d • Starts with
?email=joe*
• Ends with
?email=*company.com
• Contains ?email=*foo*
![Page 78: Designing a beautiful REST json api](https://reader034.vdocuments.site/reader034/viewer/2022052509/55cc7d9dbb61ebff138b467d/html5/thumbnails/78.jpg)
@lhazlewood
Search cont’d
• Range queries
“all accounts created between September 1st and the 15th” .../accounts?createdAt=[2014-09-01,2014-09-15]
![Page 79: Designing a beautiful REST json api](https://reader034.vdocuments.site/reader034/viewer/2022052509/55cc7d9dbb61ebff138b467d/html5/thumbnails/79.jpg)
@lhazlewood
Many To Many
![Page 80: Designing a beautiful REST json api](https://reader034.vdocuments.site/reader034/viewer/2022052509/55cc7d9dbb61ebff138b467d/html5/thumbnails/80.jpg)
@lhazlewood
Group to Account
• A group can have many accounts • An account can be in many groups • Each mapping is a resource:
GroupMembership
![Page 81: Designing a beautiful REST json api](https://reader034.vdocuments.site/reader034/viewer/2022052509/55cc7d9dbb61ebff138b467d/html5/thumbnails/81.jpg)
@lhazlewood
GET /groupMemberships/23lk3j2j3
200 OK
{
“meta”:{“href”: “…/groupMemberships/23lk3j2j3”},
“account”: { “meta”:{“href”: “…”}
},
“group”: {
“meta”{“href”: “…”}
}, …
}
![Page 82: Designing a beautiful REST json api](https://reader034.vdocuments.site/reader034/viewer/2022052509/55cc7d9dbb61ebff138b467d/html5/thumbnails/82.jpg)
@lhazlewood
GET /accounts/x7y8z9 200 OK
{
“meta”:{“href”: “…/accounts/x7y8z9”},
“givenName”: “Tony”,
“surname”: “Stark”,
…,
“groups”: { “meta”:{“href”: “…/accounts/x7y8z9/groups”}
},
“groupMemberships”: {
“meta”:{“href”: “…/groupMemberships?accountId=x7y8z9”}
}
}
![Page 83: Designing a beautiful REST json api](https://reader034.vdocuments.site/reader034/viewer/2022052509/55cc7d9dbb61ebff138b467d/html5/thumbnails/83.jpg)
@lhazlewood
Async or Long-Lived Operations
![Page 85: Designing a beautiful REST json api](https://reader034.vdocuments.site/reader034/viewer/2022052509/55cc7d9dbb61ebff138b467d/html5/thumbnails/85.jpg)
@lhazlewood
204 Accepted Location: /emails/23Sd932sSl { “status”: “queued”, ...
}
![Page 86: Designing a beautiful REST json api](https://reader034.vdocuments.site/reader034/viewer/2022052509/55cc7d9dbb61ebff138b467d/html5/thumbnails/86.jpg)
@lhazlewood
GET /emails/23Sd932sSl Expires: 2014-09-29T18:00:00.000Z { “status”: “sent”, ...
}
![Page 87: Designing a beautiful REST json api](https://reader034.vdocuments.site/reader034/viewer/2022052509/55cc7d9dbb61ebff138b467d/html5/thumbnails/87.jpg)
@lhazlewood
Batch Operations
![Page 88: Designing a beautiful REST json api](https://reader034.vdocuments.site/reader034/viewer/2022052509/55cc7d9dbb61ebff138b467d/html5/thumbnails/88.jpg)
@lhazlewood
• Each batch reflects a resource
• Batches are likely to be a collection
• Batches are likely to have a status
• Batch deletes easier than create/update
![Page 89: Designing a beautiful REST json api](https://reader034.vdocuments.site/reader034/viewer/2022052509/55cc7d9dbb61ebff138b467d/html5/thumbnails/89.jpg)
@lhazlewood
Batch Delete
“Delete all company.com accounts” DELETE /accounts?
email=*@company.com
![Page 90: Designing a beautiful REST json api](https://reader034.vdocuments.site/reader034/viewer/2022052509/55cc7d9dbb61ebff138b467d/html5/thumbnails/90.jpg)
@lhazlewood
Batch Create / Update
Already have a CollecEon concept. Use it.
![Page 91: Designing a beautiful REST json api](https://reader034.vdocuments.site/reader034/viewer/2022052509/55cc7d9dbb61ebff138b467d/html5/thumbnails/91.jpg)
@lhazlewood
Batch Create or Update POST /accounts { “items”: [ { ... account 1 ... }, { ... account 2 ... }, ... ] }
![Page 92: Designing a beautiful REST json api](https://reader034.vdocuments.site/reader034/viewer/2022052509/55cc7d9dbb61ebff138b467d/html5/thumbnails/92.jpg)
@lhazlewood
Batch Operations: The ‘Catch’
Caching is bypassed enErely L
![Page 93: Designing a beautiful REST json api](https://reader034.vdocuments.site/reader034/viewer/2022052509/55cc7d9dbb61ebff138b467d/html5/thumbnails/93.jpg)
@lhazlewood
204 Accepted Location: /batches/a1b2c3 { “status”: “processing”, //overall status “size”: “n”, “limit”: 25, ..., “items”: { { response 1 (w/ individual status) ...}, { response 2 (w/ individual status) ...}, ... } }
![Page 94: Designing a beautiful REST json api](https://reader034.vdocuments.site/reader034/viewer/2022052509/55cc7d9dbb61ebff138b467d/html5/thumbnails/94.jpg)
@lhazlewood
Errors
![Page 95: Designing a beautiful REST json api](https://reader034.vdocuments.site/reader034/viewer/2022052509/55cc7d9dbb61ebff138b467d/html5/thumbnails/95.jpg)
@lhazlewood
• As descriptive as possible • As much information as possible • Developers are your customers
![Page 96: Designing a beautiful REST json api](https://reader034.vdocuments.site/reader034/viewer/2022052509/55cc7d9dbb61ebff138b467d/html5/thumbnails/96.jpg)
@lhazlewood
POST /directories 409 Conflict { “status”: 409, “code”: 40924, “property”: “name”, “message”: “A Directory named ‘Avengers’ already exists.”, “developerMessage”: “A directory named ‘Avengers’ already exists. If you have a stale local cache, please expire it now.”, “moreInfo”: “https://www.stormpath.com/docs/api/errors/40924” }
![Page 97: Designing a beautiful REST json api](https://reader034.vdocuments.site/reader034/viewer/2022052509/55cc7d9dbb61ebff138b467d/html5/thumbnails/97.jpg)
@lhazlewood
Security
![Page 98: Designing a beautiful REST json api](https://reader034.vdocuments.site/reader034/viewer/2022052509/55cc7d9dbb61ebff138b467d/html5/thumbnails/98.jpg)
@lhazlewood
Avoid sessions when possible Authenticate every request if necessary Stateless
Authorize based on resource content, NOT URL!
Use Existing Protocol:
Oauth 1.0a, Oauth2, Basic over SSL only Custom Authentication Scheme:
Only if you provide client code / SDK Only if you really, really know what you’re doing
Use API Keys instead of Username/Passwords
![Page 99: Designing a beautiful REST json api](https://reader034.vdocuments.site/reader034/viewer/2022052509/55cc7d9dbb61ebff138b467d/html5/thumbnails/99.jpg)
@lhazlewood
401 vs 403
• 401 “Unauthorized” really means Unauthenticated “You need valid credentials for me to respond to this request”
• 403 “Forbidden” really means Unauthorized “Sorry, you’re not allowed!”
![Page 100: Designing a beautiful REST json api](https://reader034.vdocuments.site/reader034/viewer/2022052509/55cc7d9dbb61ebff138b467d/html5/thumbnails/100.jpg)
@lhazlewood
HTTP Authentication Schemes
• Server response to issue challenge:
WWW-Authenticate: <scheme name> realm=“Application Name” • Client request to submit credenEals:
Authorization: <scheme name> <data>
![Page 101: Designing a beautiful REST json api](https://reader034.vdocuments.site/reader034/viewer/2022052509/55cc7d9dbb61ebff138b467d/html5/thumbnails/101.jpg)
@lhazlewood
API Keys
• Entropy • Password Reset • Independence • Scope • Speed • Limited Exposure • Traceability
![Page 102: Designing a beautiful REST json api](https://reader034.vdocuments.site/reader034/viewer/2022052509/55cc7d9dbb61ebff138b467d/html5/thumbnails/102.jpg)
@lhazlewood
IDs
![Page 103: Designing a beautiful REST json api](https://reader034.vdocuments.site/reader034/viewer/2022052509/55cc7d9dbb61ebff138b467d/html5/thumbnails/103.jpg)
@lhazlewood
• IDs should be opaque • Should be globally unique • Avoid sequential numbers (contention,
fusking) • Good candidates: UUIDs, ‘Url64’
![Page 104: Designing a beautiful REST json api](https://reader034.vdocuments.site/reader034/viewer/2022052509/55cc7d9dbb61ebff138b467d/html5/thumbnails/104.jpg)
@lhazlewood
HTTP Method Overrides
![Page 105: Designing a beautiful REST json api](https://reader034.vdocuments.site/reader034/viewer/2022052509/55cc7d9dbb61ebff138b467d/html5/thumbnails/105.jpg)
@lhazlewood
POST /accounts/x7y8z9?_method=DELETE
![Page 106: Designing a beautiful REST json api](https://reader034.vdocuments.site/reader034/viewer/2022052509/55cc7d9dbb61ebff138b467d/html5/thumbnails/106.jpg)
@lhazlewood
Caching & Concurrency Control
![Page 107: Designing a beautiful REST json api](https://reader034.vdocuments.site/reader034/viewer/2022052509/55cc7d9dbb61ebff138b467d/html5/thumbnails/107.jpg)
@lhazlewood
Server (iniEal response): ETag: "686897696a7c876b7e”
Client (later request):
If-None-Match: "686897696a7c876b7e” Server (later response):
304 Not Modified
![Page 108: Designing a beautiful REST json api](https://reader034.vdocuments.site/reader034/viewer/2022052509/55cc7d9dbb61ebff138b467d/html5/thumbnails/108.jpg)
@lhazlewood
Maintenance
![Page 109: Designing a beautiful REST json api](https://reader034.vdocuments.site/reader034/viewer/2022052509/55cc7d9dbb61ebff138b467d/html5/thumbnails/109.jpg)
@lhazlewood
Use HTTP Redirects Create abstraction layer / endpoints when migrating Use well defined custom Media Types
![Page 110: Designing a beautiful REST json api](https://reader034.vdocuments.site/reader034/viewer/2022052509/55cc7d9dbb61ebff138b467d/html5/thumbnails/110.jpg)
@lhazlewood
.com • Free for developers • Eliminate months of development • Automatic security best practices • Single Sign On for your apps • API Authentication & Key Management • Token Authentication for SPAs / Mobile • Authorization
Libraries and integraEons: h`ps://docs.stormpath.com