- Nouns are good; verbs are bad
- Keep your base URL simple and intuitive
- Only 2 base URLs per resource
- For a collection
/dogs
- For a specific element
/dogs/1234
- Keep verbs out of your base URLs
- Use HTTP verbs to operate on the collections and elements
POST, GET, PUT and DELETE: CRUD (Create-Read-Update-Delete)
Resource | POST (create) | GET (read) | PUT (update) | DELETE (delete) |
---|---|---|---|---|
/dogs | Create a new dog | List dogs | Bulk update dogs | Delete all dogs |
/dogs/1234 | Error | Show Dog | If exists update Dog If not error | Delete Dog |
- Use plural nouns (avoid a mixed model in wich you use singular for some resources and plural for others)
- Concrete names are better than abstract (/items, /assets)
/blogs
/videos
/news
/articles
-
Simplify associations - sweep complexity under the '?'
-
To get all the dogs belonging to a specific owner
GET /owners/5678/dogs
- To create a new dog for that owner
POST /owners/5678/dogs
- Now, the relationships can be complex. Owners have relationships with veterinarians, who have relationships with dogs, who have relationships with food, and so on. It's not uncommon to see people string these together making a URL 5 or 6 levels deep. Remember that once you have the primary key for one level, you usually don't need to include the levels above because you've already got your specific object. In other words, you shouldn't need too many cases where a URL is deeper than what we have above.
/resource/identifier/resource
- Make it simple for developers to use the base URL by putting optional states and attributes behind the HTTP question mark. To get all red dogs running in the park:
GET /dogs?color=red&state=running&location=park
- Use HTTP status codes
- Use a small subset of the HTTP status codes:
200 - OK (Everything worked - success)
400 - Bad Request (The application did something wrong - client error)
500 - Internal Server Error (The API did something wrong - server error)
- If you're not comfortable reducing all your error conditions to these 3, try picking among these additional 5:
201 - Created
304 - Not Modified
404 - Not found
401 - Unauthorized
403 - Forbidden
- Make messages returned in the payload as verbose as possible
{"developerMessage" : "Verbose, plain language description of
the problem for the app developer with hints about how to fix
it.", "userMessage":"Pass this message on to the app user if
needed.", "errorCode" : 12345, "more info":
"http://dev.teachdogrest.com/errors/12345"}
In summary, be verbose and use plain language descriptions. Add as many hints as your API team can think of about what's causing an error.
- Never release an API without a version and make the version mandatory
/v1/dogs
/v2/dogs
-
Use a simple ordinal number. Don't use the dot notation like v1.2 because it implies a granularity of versioning that doesn't work weel with APIs -- It's an interface not an implementation. Stick with v1, v2, and so on.
-
How many versions should you maintain? Maintain at least one version back.
-
For how long should you maintain a version? Give developers at least one cycle to react before obsoleting a version.
- Add optional fields in a comma-delimited list:
/dogs?fields=name,color,location
- It's simple to read
- A developer can select just the information an app needs at a given time
- It cuts down on bandwidth issues, wich is important for mobile apps.
- The partial selection syntax can also be used to include associated resources cutting down on the number of requests needed to get the required information.
- Use limit and offset
/dogs?limit=25&offset=50
- You must include metadata with each response that is paginated that indicated to the developer the total number of records available.
- Defaults values: limit=10&offset=0
- Use verbs not nouns
- For example: Calculate, Translate, Convert
/convert?from=EUR&to=CNY&amount=100
- Make it clear in your API documentation that these "non-resource" scenarios are different.
- Adds a sufix to define the format
- Use JSON as default
GET /dogs (returns JSON)
GET /dogs.json (returns JSON)
GET /dogs.xml (returns XML)
- Follow JavaScript conventions for naming attributes.
- Use medial capitalization - CamelCase.
- Use uppercase or lowercase depending on type of object.
"createdAt": 1320296464
- Global search: (?q represents the query)
/search?q=fluffy+fur
/search.xml?q=fluffy+fur
- Scoped search
/owners/5678/dogs?q=fluffy+fur
Lets take a look at what some API requests and responses look like for our dogs API. Create a brown dog named Al
POST /dogs
name=Al&furColor=brown
Response
200 OK
{
"dog":{
"id": "1234",
"name": "Al",
"furColor": "brown"
}
}
Rename Al to Rover - Update
PUT /dogs/1234
name=Rover
Response
200 OK
{
"dog":{
"id":"1234",
"name": "Rover",
"furColor": "brown"
}
}
Tell me about a particular dog
GET /dogs/1234
Response
200 OK
{
"dog":{
"id":"1234",
"name": "Rover",
"furColor": "brown"
}
}
Tell me about all the dogs
GET /dogs
Response
200 OK
{
"dogs":
[{"dog":{
"id":"1233",
"name": "Fido",
"furColor": "white"}},
{"dog":{
"id":"1234",
"name": "Rover",
"furColor": "brown"}}]
"_metadata":
[{"totalCount":327,"limit":25,"offset":100}]
}
Delete Rover :-(
DELETE /dogs/1234
Response
200 OK
MULLOY, Brian. Web API Design: Crafting Interfaces that Developers Love. Apigee.