Hosted by Three Crickets

Scalable REST Platform
For the JVM

Prudence logo: bullfinch in flight

URI-space Architecture

REST does not standardize URI-spaces, and indeed has little to say about URI design. However, it does imply a preference for certain architectural principles. We go over much of the impetus in The Case for REST article, and suggest you start there.
It's a good idea to think very carefully about your URI-space: a good design will in turn help you define well-encapsulated RESTful resources. Below are some topics to consider.

Nouns vs. Verbs

It's useful to think of URIs as syntactic nouns, a grammatical counterpart to HTTP's verbs. To put it simply: make sure that you do not include verbs in your URIs. Examples:
What is wrong with verbs in URIs?
One potential problem is clarity. Which HTTP verb should be used on a verb URI? Do you need to POST, PUT or DELETE to "/service/{id}/stop/" in order to stop the service? Of course, you can support all and document this, but it won't be immediately obvious to the user.
A second potential problem is that you need to keep increasing the size of your URI-space the more actions of this sort you want to support. This means more files, more classes, and generally more code. Handling these operations inside a single resource would just mean a simple "if" or "switch" statement and an extra method.
A third, more serious potential problem is idempotency. The idempotent verbs PUT and DELETE may be optimized by the HTTP infrastructure (for example, a smart load balancer) such that requests arrive more than once: this is allowed by the very definition of idempotency. However, your intended operations may not be semantically idempotent. For example, if a "stop" is sent to an already-stopped service, it may return an "already stopped" 500 error. In this case, if the infrastructure indeed allows for two "stop" commands to come through, then the user may get an error even though the operation succeeded for the first "stop." There's an easy way around this: simply allow only POST, the non-idempotent verb, for all such operations. The infrastructure would never allow more than request to come through per POST. However, if you enforce the use of POST, you will lose the ability of the infrastructure to optimize for non-idempotency. POST is the least scalable HTTP verb.
The bottom line is that if you standardize on only using nouns for your URIs, you will avoid many of these semantic pitfalls.
Also: beware of gerunds! A URI such as "/service/{id}/stopping/" is may be a noun, but allows for some verb-related problems to creep in.

Do You Really Need REST?

In the above section, it was suggested that you prefer nouns to verbs. However, this preference may be too constraining for your project. Your application may be very command-oriented, such that you will end up with a very small set of "noun" URIs that need to support a large amount of commands, meaning less clarity and more cluttered code.
REST's best feature it it's tiny set of tightly defined verbs: GET, POST, PUT, DELETE. The entire infrastructure is highly optimized around them: load balancers, caches, browsers, gateways, etc., all know how best to handle each of these for maximum scalability and reliability. But, it's entirely possible that your needs cannot be easily satisfied by just four verbs.
And that's OK. REST is not always the best solution for APIs.
Instead, take a look at RPC (Remote Procedure Call) mechanisms. The Diligence framework, based on Prudence, provides easy and robust support for both JSON-RPC and XML-RPC in its RPC Service as well as Ext Direct in its Sencha Integration, allowing you to hook a JavaScript function on the server directly to a URI. In terms of HTTP, these protocols all use HTTP POST, and do not leverage the HTTP infrastructure as well as a more fully RESTful API. But, one size does not fit all, and an RPC-based solution may prove a better match for your project.
It's also perfectly possible to use both REST and RPC in your project. Use each approach where it is most appropriate.

Hierarchical URIs

It's entirely a matter of convention that the use of "/" in URIs implies hierarchy. Historically, the convention was likely imported from filesystem paths, where a name before a "/" signifies a directory rather than a file.
This convention is useful because it's very familiar to users, but beyond that it implies a few semantic properties that can add clarity and power to your resource design. There are two possible principles you may consider:
  1. A descendant resource belongs to its ancestor, such that resources have cascading relationships in the hierarchy. This implies two rules:
    1. Operations on a resource may affect descendants. This rule is most obvious when applied to the DELETE verb: for example, if you delete "/user/{id}/", then it is expected that the resources at "/user/{id}/profile/" and "/user/{id}/preferences/" also be deleted. A PUT, too, would also affect the descendant resources.
    2. Operations on a resource should not affect ancestors. In other words, a descendant's state is isolated from its ancestors. For example, if I send a POST to "/user/{id}/profile/", the representation at "/user/{id}/" should remain unaltered.
  2. A descendant resource belongs to its ancestor and also represents an aspect of its ancestor, such that operations on a resource can be fine-tuned to particular aspects of it. This implies three rules:
    1. Descendant representations are included in ancestor representations. For example, a GET on "/service/{id}/" would include information about the status that you would see if you GET on "/service/{id}/status/". The latter URI makes it easier for the client to direct operations at the status aspect.
    2. Operations on a resource may affect descendants. See above.
    3. Operations on a resource will affect ancestors. This is the opposite of the above: the descendant's state is not isolated from its ancestors. For example, a POST to "/service/{id}/status/" would surely also affect "/service/{id}/", which includes the status.
You can see from the difference between rule 1.b and 2.c. that it's important to carefully define the nature of your hierarchical relationships. Unlike filesystem directory hierarchies, in a URI-space there is no single standard or obvious interpretation of what of a hierarchical relationship would mean. But unless you clarify it for yourself, it cannot be clear to your users.

Formats Are Not Aspects

A format should not be considered "an aspect" in the sense used in principle 2. For example, "/service/{id}/html/" would not be a good way to support an HTML format for "/service/{id}/". The reason is that you would be allowing for more than one URI for the same encapsulated resource, creating confusion for users. For example, it's not immediately clear what would happen if they DELETE "/service/{id}/html/". Would that just remove the ability to represent the service as HTML? Or delete the service itself?
Supporting multiple formats is best handled with content negotiation, within the REST architecture. If further formatting is required, URI query parameters can be used. For example: "/service/{id}/?indent=2" might return a JSON representation with 2-space indentation.

Plural vs. Singular

You'll see RESTful implementations that use either convention. The advantage of using the singular form is that you have less addresses, and what some people would call a more elegant scheme:
/animal/cat/12/ -> Just one cat
/animal/cat/ -> All cats
Why add another URL format when a single one is enough to do the work? One reason is that you can help the client avoid potential errors. For example, the client probably uses a variable to hold the ID of the cat and then constructs the URL dynamically. But, what if the client forgets to check for empty IDs? It might then construct a URL in the form "/animal/cat//" which would then successfully access all cats. This can cause unintended consequences and be difficult to debug. If, however, we used this scheme:
/animal/cat/12/ -> Just one cat
/animal/cats/ -> All cats
…then the form "/animal/cat//" would route to our singular cat resource, which would indeed not find the "empty" cat and return the expected, debuggable 404 error.
From this example, we can extract a good rule of thumb: clearly separate URI templates by usage, so that mistakes cannot happen. More URI types means more debuggability.