URL design discussions for RESTful web services often degrade into debates over pluralization and parameter names. There are a couple of principles I like to use to keep things simple.
1) Using your API should feel like using a filesystem
- Endpoints used to create, list, and search for entities should look like directories, e.g.
/users
- Use a plural noun so it feels like a directory of users, not a user controller
- Endpoints used to read, update, and delete individual entities should look like files, e.g.
/users/charlie
2) All calls to a given endpoint should return the same type
- Either apples, or oranges, or a list of oranges, don’t mix them up
- File-looking endpoints should return individual entities
- Directory-looking endpoints should return lists of entities
We may be bikeshedding here but I think your API will be more intuitive to newcomers if you model it this way.
- Consistent response type per endpoint simplifies deserialization for clients, no switching needed
- Once you agree on a contract it’s easy to mock up with static files on a server
- Clients can start working with your mockup before your code is finished
Example Operations on Endpoints that look like Files
Read
GET /users/charlie
200 OK
{username: charlie, state: VA}
Update
PUT|PATCH /users/charlie
[password=1111]
Delete
DELETE /users/charlie
Example Operations on Endpoints that Look Like Directories
List
GET /users?start=40&count=20
200 OK
[{username: charlie, state: VA}, ...]
Search
GET /users?q=cha
Create
POST /users
{username=charlie&password=1234}
201 CREATED
Location: /users/charlie
Directory endpoints are supposed to return lists (or nothing). So send back a pointer to the new user record, rather than the user data itself.
PUT /users/charlie
Also ok IFF you allow clients to generate entity ids
Serious Bikeshedding
Here’s other stuff I like:
Use a filename extension instead of the Accept header to express the response format:
http://api.example.com/users/charlie.json
- Not pure REST but it makes your API easier for devs to poke with curl and the browser. Easier to use means better adoption!
- Friendlier on stupid caches that improperly handle headers since the endpoint always sends back the same bytes (within the ttl)
- You can use .html (or no extension) to request the web representation and serve your API and web views with the same controller logic (though this can turn into a rabbit hole)
Another vote against the Accept header: version your endpoints in the URL, at web-application level:
http://q.addthis.com/feeds/1.0/trending.json?pubid=atblog
- feeds.war is the webapp and 1.0 is the version of the released artifact (which is 1.0.3 internally)
- Version numbers should correspond to deployable artifacts so they’re easier to manage
- You’ll need to keep old versions online in real-world use cases
Dropwizard has a nice model for organizing your projects.
Some javascript/flash API consumers will pressure you to add a suppress_response_codes parameter and always send them a 200. You’ll hate it but will end up giving in (just wait).
- You’ll need to define a standard error response envelope that they can switch on to check for errors and extract the reason. It will get messy for them and they will lose a lot of the benefits of this design.
- Anyone have better ideas here?
I don’t understand why “makes your API easier for devs to poke with … the browser” is so important at this point.
With a plethora of useful ReST/HTTP plugins/tools (e.g. search “rest client” in the Chrome Web Store”), why should exploring using the lowest common denominator dictate your technical decisions?
The browser is most likely the tool you used to discover the API and the tool you are using to read the docs for the API, thus it completely makes sense to make it the tool you use to quickly play with the API and decide if you’re going to use. I completely agree with Charlie here.
In terms of the API user experience it’s pretty sweet to be able to link users to actual calls from within your online docs. If you have good linking between your endpoints you can do a fair bit of navigation around the API in your browser before you need to reach for curl or fancier clients. It makes for a better first impression which improves the likelihood that someone will implement.
What about the best URL designed in REST as is “/”?
API URLs don’t matter when everything is hyperlinked.
(As in a Website, who likes to type every single page link in the browser?)
Pingback: URL Design for RESTful Web Services | Output Translation
I think POST /users/charlie should be a PUT (more bikeshedding).
Creation can happen by either POSTING to the generic resource /users or PUTting to a specific instance (if it’s allowed for the client to name the resource, as you point out).
Ah, good catch. PUT /users/charlie is what I meant to say, error on my part 🙂
Maybe it would be a good idea to fix the article than, not everyone will read every single comment… Other than that, thanks for the really good post.
done!
Pingback: Review: Google Glass API | API UX
http://hadihariri.com/2013/07/29/one-step-guide-on-designing-a-rest-api/
Pingback: URL Design for RESTful Web Services | AddThis Blog