Endpoints in the NARWHL framework are represented by URIs that point to resources. These endpoints will generally return one of two kinds of data:
- If a unique identifier/ID is specified, the URI should respond with a representation of the individual resource tied to that identifier, along with links to related resources.
- If no identifier/ID is specified, the URI should respond with a list of resources available to the calling application along with links to their related resources. Parameters may be passed on the query string to define criteria to narrow the search results.
When converting nouns to resource endpoints, the convention is to use the common name of the resource (i.e. “Products”) and convert it to its lowercase plural form (i.e. “products”). The plural is preferred even when an identifier is passed or in any other situation when only a single resource might be returned.
The general format of URI endpoints in the NARWHL framework follows this familiar structure:
For example, the endpoint
should return a representation of the Product the system has assigned the ID 73924. Similarly
should return a list of products currently available to the developer. In both cases, links to additional details and related resources should be included. This allows for easier discovery of additional data and enables a well-built client to learn about new endpoints and data as they are made available.
The values used for resource identifiers should be constrained as unique and unchanging. An endpoint URI pointing to a specific resource should be cacheable and never expected to point to anything else, regardless of the context of the call. If the structure of the URI ever needs to change, the old URI should return an HTTP error 301 or 302 with a Location header pointing to the new URI. Values that are considered to be editable by the system should never be used as resource identifiers.
But identifiers need not necessarily be numerical. For example, user names, automobile VINs and publishing ISBNs each contain a mix of alphanumeric characters. They also are already constrained as unique to one user, car or book respectively, so they are suitable for use in the URI to point to their associated resource.
Resources may also be accessible through their relationships with other resources. For example, Products may belong to one or more Categories. To obtain a list of all Products under the Category ID 36, you could use the URI
This would return only that list of Products under that Category, where a call to
Would return all possible Products (assuming your API allowed for that).
A business case may exist for some resources to be accessed directly through their relationship with other resources. For example, an Order may contain many Line Items, each pointing to a Product they represent but carrying the quantity, final price and other Order-specific information. In this case, each item will likely be accessed through it’s relationship with an Order as opposed to being accessed at the root level (i.e. “/line_items/12345”). The URI for this resource would then resemble
To aid in clarity when reading logs or for other business reasons, resources may also be accessed through their relations, even though they may have root-level representations. For example, the URI for the Product with ID 73924 as described before could also be expressed as
In this way, a single resource may be accessed through multiple endpoints. In each case, the resource must respond with the same representation – the response data and format should not change based on the context in which it is called. For the sake of client caching, however, a single endpoint should serve and be identified as the canonical version of that resource. For example, the URI
Should respond with a link header pointing to the canonical URI for this resource:
Link: <http://api.example.com/v1/products/73924>; rel=”canonical”
Why does URI Design Matter?
Including links within resource responses to indicate relationships between resources should preclude any need for the client developer to hardcode or – for that matter – even need to know the URI format for accessing a resource. However, it’s imprudent to assume that some human intervention won’t be needed either on the client or server side at some point, particularly when troubleshooting an error.
Working with URIs that reflect the structure and intent of the application can save time when troubleshooting issues. The less parsing a developer will have to do at this stage just to understand what’s happening, the better the overall experience. With this in mind, well-designed endpoints are crucial to enhancing the overall developer experience.