KEMBAR78
Design Beautiful REST + JSON APIs | PPTX
Beautiful REST + JSON APIs

Les Hazlewood @lhazlewood
CTO, Stormpath
stormpath.com
.com
• Identity Management and
Access Control API
• Security for your applications
• User security workflows
• Security best practices
• Developer tools, SDKs, libraries
Outline
• APIs, REST & JSON
• REST Fundamentals
• Design
Base URL
Versioning
Resource Format
Return Values
Content Negotiation
References (Linking)
Pagination
Query Parameters
Associations

Errors
IDs
Method Overloading
Resource Expansion
Partial Responses
Caching & Etags
Security
Multi Tenancy
Maintenance
APIs
•
•
•
•
•

Applications
Developers
Pragmatism over Ideology
Adoption
Scale

Learn more at Stormpath.com
Why REST?
•
•
•
•
•
•

Scalability
Generality
Independence
Latency (Caching)
Security
Encapsulation

Learn more at Stormpath.com
Why JSON?
•
•
•
•
•

Ubiquity
Simplicity
Readability
Scalability
Flexibility

Learn more at Stormpath.com
HATEOAS
•
•
•
•
•
•
•

Hypermedia
As
The
Engine
Of
Application
State

Further restriction on REST architectures.
Learn more at Stormpath.com
REST Is Easy

Learn more at Stormpath.com
REST Is *&@#$! Hard
(for providers)

Learn more at Stormpath.com
REST can be easy
(if you follow some guidelines)

Learn more at Stormpath.com
Example Domain: Stormpath
•
•
•
•
•
•

Applications
Directories
Accounts
Groups
Associations
Workflows
Fundamentals

Learn more at Stormpath.com
Resources
Nouns, not Verbs
Coarse Grained, not Fine Grained
Architectural style for use-case scalability

Learn more at Stormpath.com
What If?
/getAccount
/createDirectory
/updateGroup
/verifyAccountEmailAddress
Learn more at Stormpath.com
What If?
/getAccount
/getAllAccounts
/searchAccounts
/createDirectory
/createLdapDirectory
/updateGroup
/updateGroupName
/findGroupsByDirectory
/searchGroupsByName
/verifyAccountEmailAddress
/verifyAccountEmailAddressByToken
…
Smells like bad RPC. DON‟T DO THIS.
Learn more at Stormpath.com
Keep It Simple

Learn more at Stormpath.com
The Answer
Fundamentally two types of resources:
Collection Resource
Instance Resource

Learn more at Stormpath.com
Collection Resource

/applications

Learn more at Stormpath.com
Instance Resource

/applications/a1b2c3

Learn more at Stormpath.com
Behavior
•
•
•
•
•

GET
PUT
POST
DELETE
HEAD

Learn more at Stormpath.com
Behavior
POST, GET, PUT, DELETE

≠ 1:1
Create, Read, Update, Delete

Learn more at Stormpath.com
Behavior
As you would expect:
GET = Read
DELETE = Delete
HEAD = Headers, no Body

Learn more at Stormpath.com
Behavior
Not so obvious:
PUT and POST can both be used for
Create and Update

Learn more at Stormpath.com
PUT for Create
Identifier is known by the client:
PUT /applications/clientSpecifiedId
{
…

}

Learn more at Stormpath.com
PUT for Update
Full Replacement
PUT /applications/existingId
{
“name”: “Best App Ever”,
“description”: “Awesomeness”
}
Learn more at Stormpath.com
PUT

Idempotent

Learn more at Stormpath.com
POST as Create
On a parent resource
POST /applications
{
“name”: “Best App Ever”
}
Response:
201 Created
Location: https://api.stormpath.com/applications/a1b2c3

Learn more at Stormpath.com
POST as Update
On instance resource
POST /applications/a1b2c3

{
“name”: “Best App Ever. Srsly.”
}

Response:
200 OK

Learn more at Stormpath.com
POST

NOT Idempotent

Learn more at Stormpath.com
Media Types
• Format Specification + Parsing Rules
• Request: Accept header
• Response: Content-Type header
•
•
•
•

application/json
application/foo+json
application/foo+json;application
…

Learn more at Stormpath.com
Design Time!

Learn more at Stormpath.com
Base URL

Learn more at Stormpath.com
http(s)://api.foo.com
vs
http://www.foo.com/dev/service/api/rest

Learn more at Stormpath.com
http(s)://api.foo.com
Rest Client
vs
Browser
Learn more at Stormpath.com
Versioning

Learn more at Stormpath.com
URL
https://api.stormpath.com/v1
vs.
Media-Type
application/foo+json;application&v=1
Learn more at Stormpath.com
Resource Format

Learn more at Stormpath.com
Media Type
Content-Type: application/json
When time allows:
application/foo+json
application/foo+json;bar=baz&v=1

…
Learn more at Stormpath.com
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.
Learn more at Stormpath.com
Date/Time/Timestamp
There‟s already a standard. Use it: ISO 8601
Example:
{
…,
“createdTimestamp”: “2012-07-10T18:02:24.343Z”
}
Use UTC!
Learn more at Stormpath.com
Response Body

Learn more at Stormpath.com
GET obvious
What about POST?

Return the representation in the response
when feasible.
Add override (?_body=false) for control
Learn more at Stormpath.com
Content Negotiation

Learn more at Stormpath.com
Header
• Accept header
• Header values comma delimited in order
of preference
GET /applications/a1b2c3
Accept: application/json, text/plain

Learn more at Stormpath.com
Resource Extension
/applications/a1b2c3.json
/applications/a1b2c3.csv
…
Conventionally overrides Accept header

Learn more at Stormpath.com
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
Learn more at Stormpath.com
Instance w/ HREF (v1)
GET /accounts/x7y8z9
200 OK
{
“href”: “https://api.stormpath.com/v1/accounts/x7y8z9”,
“givenName”: “Tony”,
“surname”: “Stark”,
...
}

Learn more at Stormpath.com
Resource References
aka „Linking‟
(v1)

Learn more at Stormpath.com
• Hypermedia is paramount.
• Linking is fundamental to scalability.
• Tricky in JSON
• XML has it (XLink), JSON doesn‟t
• How do we do it?

Learn more at Stormpath.com
Instance Reference (v1)
GET /accounts/x7y8z9
200 OK
{
“href”: “https://api.stormpath.com/v1/accounts/x7y8z9”,
“givenName”: “Tony”,
“surname”: “Stark”,
…,
“directory”: ????
}

Learn more at Stormpath.com
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”
}
}

Learn more at Stormpath.com
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”
}
}

Learn more at Stormpath.com
Linking v2
(recommended)

Learn more at Stormpath.com
Instance HREF (v2)
GET /accounts/x7y8z9
200 OK
{
“meta”: {
“href”: “https://api.stormpath.com/v1/accounts/x7y8z9”,
“mediaType”: “application/ion+json;version=2&schema=...”
},
“givenName”: “Tony”,
“surname”: “Stark”,
…
}

Learn more at Stormpath.com
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;version=2&schema=...”
}
}
}

Learn more at Stormpath.com
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/ioncoll+json;version=2&schema=...”
}
}
}

Learn more at Stormpath.com
Reference Expansion
(aka Entity Expansion, Link Expansion)

Learn more at Stormpath.com
Account and its Directory?

Learn more at Stormpath.com
GET /accounts/x7y8z9?expand=directory
200 OK
{
“meta”: {...},
“givenName”: “Tony”,
“surname”: “Stark”,
…,
“directory”: {
“meta”: { ... },
“name”: “Avengers”,
“description”: “Hollywood’s hope for more $”,
“creationDate”: “2012-07-01T14:22:18.029Z”,
…
}
}

Learn more at Stormpath.com
Partial Representations

Learn more at Stormpath.com
GET
/accounts/x7y8z9?fields=givenName,surname,
directory(name)

Learn more at Stormpath.com
Pagination

Learn more at Stormpath.com
Collection Resource supports query params:
• Offset
• Limit
…/applications?offset=50&limit=25

Learn more at Stormpath.com
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”: “…”, ...}
},
…
]
}

Learn more at Stormpath.com
Many To Many

Learn more at Stormpath.com
Group to Account
• A group can have many accounts
• An account can be in many groups
• Each mapping is a resource:
GroupMembership

Learn more at Stormpath.com
GET /groupMemberships/23lk3j2j3
200 OK
{
“meta”:{“href”: “…/groupMemberships/23lk3j2j3”},
“account”: {
“meta”:{“href”: “…”}
},
“group”: {
“meta”{“href”: “…”}
},
…
}

Learn more at Stormpath.com
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”}
}
}

Learn more at Stormpath.com
Errors

Learn more at Stormpath.com
• As descriptive as possible
• As much information as possible
• Developers are your customers

Learn more at Stormpath.com
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/4092
4”
}
Learn more at Stormpath.com
Security

Learn more at Stormpath.com
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

Learn more at Stormpath.com
401 vs 403
• 401 “Unauthorized” really means
Unauthenticated
“You need valid credentials for me to respond to
this request”

• 403 “Forbidden” really means Unauthorized
“I understood your credentials, but so sorry, you‟re
not allowed!”
Learn more at Stormpath.com
HTTP Authentication Schemes
• Server response to issue challenge:
WWW-Authenticate: <scheme name>
realm=“Application Name”
• Client request to submit credentials:

Authorization: <scheme name> <data>
Learn more at Stormpath.com
API Keys
•
•
•
•
•
•

Entropy
Password Reset
Independence
Speed
Limited Exposure
Traceability

Learn more at Stormpath.com
IDs

Learn more at Stormpath.com
• IDs should be opaque
• Should be globally unique
• Avoid sequential numbers (contention,
fusking)
• Good candidates: UUIDs, „Url64‟

Learn more at Stormpath.com
HTTP Method Overrides

Learn more at Stormpath.com
POST /accounts/x7y8z9?_method=DELETE

Learn more at Stormpath.com
Caching &
Concurrency Control

Learn more at Stormpath.com
Server (initial response):
ETag: "686897696a7c876b7e”

Client (later request):
If-None-Match: "686897696a7c876b7e”

Server (later response):
304 Not Modified

Learn more at Stormpath.com
Maintenance

Learn more at Stormpath.com
Use HTTP Redirects
Create abstraction layer / endpoints when
migrating
Use well defined custom Media Types

Learn more at Stormpath.com
.com
• Free for developers
• Eliminate months of development
• Automatic security best practices

Sign Up Now: Stormpath.com

Design Beautiful REST + JSON APIs