Beautiful REST
API Design
Brian Retterer
@bretterer
brian@stormpath.com
/getAccount?id=3
/createAccount
/deleteUser?id=3
/updateAccount?id=3&json=1
/getAllAccounts
/searchForAccount?fname=homer
/getAccountComments?id=3&return=json
/getAccount?id=3&withComments=1
/getAllAccounts?format=json
/searchForAccount?fname=homer&return=xml
/deleteAccount?id=3
/addCommentByAccount?id=3&comment=This+API+SUCKS
/getAccount?id=3&return=json
/getAccount?id=3&withComments=1&fmt=json
/getAccountComments?id=3
/updateAccount?id=3
/getAllAccounts?json=true
"A REST API should be entered with no prior knowledge beyond the initial URI (bookmark) and set of standardized media types that are appropriate for the intended audience (i.e., expected to be understood by any client that might use the API). From that point on, all application state transitions must be driven by client selection of server-provided choices that are present in the received representations or implied by the user’s manipulation of those representations." - Dr. Fielding
!= 1:1
GET = READ
DELETE = DELETE
HEAD = Headers Only, No Body
OPTIONS = All available options for endpoint
POST and PUT can be used for create and update
Identifier is known by Client:
PUT /applications/{clientSpecifiedId}
{
...
}
Full Replacement:
PUT /applications/{existingId}
{
"name": "Marvel Comics",
"description": "We are the best!"
}
On Parent Resource
POST /applications
{
"name": "DC Comics",
"description": "We are the best!"
}
On Instance Resource:
POST /applications/abc123
{
"description": "Ok Marvel is better"
}
/users
/users/3
/users/3/comments
/comments
GET: List all users
POST/PUT: Create User
GET: List the user
POST/PUT: Update the user
DELETE: Delete the user
GET: List the users Comments
POST/PUT: Create a comment for User
GET: List all comments
Search:
/users?fname=homer
Pagination:
/users?offset=1&limit=10
Accept: application/json
Content-Type: application/json
https://example.io/developer/api/public/rest
vs.
https://api.example.io
https://api.example.io/?version=1
vs.
https://api.example.io/v1
GET /users/3
200 OK
{
"meta": {"href": "https://example.io/users/3"},
"fname": "Homer",
"lname": "Simpson"
}
GET /users
200 OK
{
"meta": {"href": "https://example.io/users},
"items": [{
"meta": {"href": "https://example.io/users/3"},
"fname": "Homer",
"lname": "Simpson"
}, {
"meta": {"href": "https://example.io/users/4"},
"fname": "Bart",
"lname": "Simpson"
}]
}
GET /users/3
200 OK
{
"meta": {"href": "https://example.io/users/3},
"fname": "Homer",
"lname": "Simpson",
"comments": {
"meta": {
"href": "https://example.io/users/3/comments"
}
}
}
GET /users
200 OK
{
"meta": {"href": "https://example.io/users"},
"items": [...],
"create": {
"meta": {
"href": "https://example.io/users",
"rel": ["create-form"],
"method": "POST"
},
"items": [
{"name": "fname"},
{"name": "lname}
]
}
}
GET /users
200 OK
{
"meta": {"href": "https://example.io/users"},
"items": [...],
"create": {...}
"search": {
"meta": {
"href": "https://example.io/users",
"rel": ["search-form"],
"method": "GET"
},
"items": [
{"name": "fname"},
{"name": "lname}
]
}
}
GET /
200 OK
{
"meta": {"href": "https://example.io/"},
"users": {
"meta": {
"href": "https://example.io/users",
"rel": ["collection"]
}
}
}
POST /users
409 Conflict
{
"status": 409,
"code": 40924,
"property": "email",
"message": "A user with the email `homer@thesimpsons.com` already exists.",
"developerMessage": "A user with the email `homer@thesimpsons`
already exists. If you have a stale local cache,
please expire it now",
"moreInfo": "https://docs.example.io/errors/40924"
}
Date/Time/Timestamp
{
...
"createdAt": "2016-10-19T17:55:00.123Z"
...
}
Use UTC (Zulu) time
Resource Extensions
https://example.io/users/3.xml
https://example.io/users/3.txt
https://example.io/users/3.html
Adding an extension will override the Accept header
Allow Method Overrides
POST /accounts/3?_method=DELETE
This should be done if you expect your users to use the web browser or some way that does not allow for anything verbs other than GET and POST
ID's
Beautiful REST
API Design
Brian Retterer
@bretterer
brian@stormpath.com