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?