This page captures thoughts and ideas on how to design URIs for WOA/RESTful web-services and applications. Technically, URI design is irrelevant as all URIs should be discovered through request-response exchanges between the user agent and the server. However, in practice, well designed URIs have been conducive to good architecture.
Example 1: URIs for versioned multilingual documents
Problem statement: imagine having a model where you have a document identified by an docId that can have different translations (one of them being the default) and revisions (each translation has its own independent revision history).
Approach 1: using URI segments
One could model these resources in the following way.
| Path | Meaning |
| /{docId} | current version in default translation |
| /{docId}/versions | list of versions in default translation (Q: should this redirect to /{docId}/translations/{lang}/versions using the default language?) |
| /{docId}/versions/{version} | specific version for default translation (NOTE: this makes no sense since each translation has its own version list) |
| /{docId}/translations | list of translations |
| /{docId}/translations/{lang} | current version for a given translation |
| /{docId}/translations/{lang}/versions | list of versions for a given translation |
| /{docId}/translations/{lang}/versions/{version} | specific version for a given translation |
Benefits
| Drawbacks
|
Approach 2: Using query parameters
Or, with the same expressive power, one might do:
| Path | Meaning |
| /{docId} | current version in default translation |
| /{docId}/versions | list of versions in default translation (Q: should this redirect to /{docId}/versions?translation={default} using the default language?) |
| /{docId}?version={v} | specific version for default translation (NOTE: this makes no sense since each translation has its own version list) |
| /{docId}/translations | list of translations |
| /{docId}?translation={lang} | current version for a given translation |
| /{docId}/versions?translation={lang} | list of versions for a given translation |
| /{docId}?translation={lang}&version={v} | specific version for a given translation |
Benefits
| Drawbacks
|
Approach 3: Using Accept-Language header and query parameters
The Accept-Language header can be used by a user agent to request resources in various language and even provide hints at suitable alternatives when the requested language is not available.
| Path | Meaning |
| /{docId} | current version of preferred translation |
| /{docId}/versions | list of versions in preferred translation |
| /{docId}?version={v} | specific version for preferred translation |
| /{docId}/translations | list of translations |
For user agents that cannot provide a custom HTTP Accept-Language request header, the API could define an override query parameter (e.g. ?accept-language=...).
Benefits
| Drawbacks
|
Addendum 1: URI discoverability
All above approaches fail to address one important aspect: URI discoverability. Without the means for the user agent to "discover" the various URIs that can be used to interact with the document, the user agent must hard-code the URI design. Such hard-coding prevents future changes the URI design or requires the server administrator to manage redirect from old designs to the latest design.
The solution is to return a hypermedia or XML document instead of the contents of the document for path /{docId}. The returned document provides descriptions for the various URIs that a document can be requested by.
| Path | Meaning |
| /{docId} | hypermedia or XML document describing the resource |
Let's assume for this example that the returned response is an XML document. It may look as follows:
<doc translation="en"> <link rel="contents">http://...</link> <link rel="versions">http://...</link> <link rel="translations">http://...</link> </doc>
Benefits
| Drawbacks
|
Note: the request-response exchange could be avoided by defining a custom MIME type (e.g. application/x.doc-meta+xml) and using the Accept request header, where the default behavior for /{docId} would be to return the current verion of the translation specified by the Accept-Language header.

Comments