Source:
(1)Learn REST: A RESTful Tutorial
(2)Representational State Transfer (REST)
Digest:
1. What is REST?
The REST architectural style describes six constraints:
- Uniform Interface
- Stateless
- Cacheable
- Client-Server
- Layered System
- Code on Demand (optional)
2. REST API Quick Tips
Whether it's technincally RESTful or not (according to the six constraints mentioned previously), here are a few recommended REST-like concepts. These six quick tips will result in better, more usable services.
(1) User HTTP Verbs to Make Your Requests Mean Something
API consumers are capable of sending GET, POST, PUT, and DELETE verbs, which greatly enhance the clarity of a given request.
Generally, the four primary HTTP verbs are used as follows:
- GET: Read a specific resource (by an identifier) or a collection of resources.
- PUT: Update a specific resource (by an identifier) or a collection of resources. Can also be used to create a specific resource if the resource identifier is known before-hand.
- DELETE: Remove/delete a specific resource by an identifier.
- POST: Create a new resource. Also a catch-all verb for operations that don't fit into the other categories.
NOTE: GET requests must not change any underlying resource data. Measurements and tracking which update data may still occur, but the resource data identified by the URI should not change.
(2) Provide Sensible Resource Names
Producing a great API is 80% art and 20% science. Creating a URL hierachy representing sensible resources is the art part. Having sensible resource names (which are just URL paths, such as /customers/12345/orders) improves the clarity of what a given request does.
Appropriate resource names provide context for a service request, increasing understandability of the API. Resources are viewed hierarchically via their URI names, offering consumers a friendly, easily-understood hierarchy of resources to leverage in their applications.
Here are some quick-hit rules for URL path (resource name) design:
- Use identifiers in your URLs instead of in the query-string. Using URL query-string parameters is fantastic for filtering, but not for resource names.
-- Good: /users/12345
-- Poor: /api?type=user&id=23
- Leverage the hierachical nature of the URL to imply structure.
- Design for your clients, not for your data.
- Resource names should be nouns. Avoid verbs as resource names, to improve clarity. Use the HTTP methods to specify the verb portion of the request.
- Use plurals in URL segments to keep your API URIs consistent across all HTTP methods, using the collection metaphor.
-- Recommended: /customers/33245/orders/8769/lineitems/1
-- Not: /customer/33245/order/8769/lineitem/1
- Avoid using collection verbiage in URLs. For example 'customer_list' as a resource. Use pluralization to indicate the collection metaphor (e.g. customers vs. customer_list).
- Use lower-case in URL segments, separating words with underscores ('_') or hyphens ('-'). Some servers ignore case so it's best to be clear.
- Keep URLs as short as possible, with as few segments as make sense.
(3) Use HTTP Response Codes to Indicate Status
Response status codes are part of the HTTP specification. There are quite a number of them to address the most common situations. In the spirit of having our RESTful services embrace the HTTP specification, our Web APIs should return relevant HTTP status codes. For example, when a resource is successfully created (e.g. from a POST request), the API should return HTTP status code 201.
Suggested usages for the "Top 10" HTTP Response Status Codes are as follows:
- 200 OK: General success status code. This is the most common code. Used to indicate success.
- 201 CREATED: Successful creation occurred (via either POST or PUT). Set the Location header to contain a link to the newly-created resource (on POST). Response body content may or may not be present.
- 204 NO CONTENT: Indicates success but nothing is in the response body, often used for DELETE and PUT operations.
- 400 BAD REQUEST: General error for when fulfilling the request would cause an invalid state. Domain validation errors, missing data, etc. are some examples.
- 401 UNAUTHORIZED: Error code response for missing or invalid authentication token.
- 403 FORBIDDEN: Error code for when the user is not authorized to perform the operation or the resource is unavailable for some reason (e.g. time constaints, etc.).
- 404 NOT FOUND: Used when the requested resource is not found, whether it doesn't exist or if there was a 401 or 403 that, for security reasons, the service wants to mask.
- 405 METHOD NOT ALLOWED: Used to indicate that the requested URL exists, but the requested HTTP methods is not applicable. For example, POST /users/12345 where the API doesn't support creation of resources this way (with a provided ID). The Allow HTTP header must be set when returning a 405 to indicate the HTTP methods that are supported. In the previous case, the header would look like "Allow: GET, PUT, DELETE".
- 409 CONFLICT: Whether a resource conflict would be caused by fulfilling the request. Duplicate entries, such as trying to create two customers with the same information, and deleting root objects when cascade-delete is not supported are a couple of examples.
- 500 INTERNAL SERVER ERROR: Never return this intentionally. The general catch-all error when the server-side throws an exception. Use this only for errors that the consumer cannot address from their end.
(4) Offer Both JSON and XML
Favor JSON support unless you're in a highly-standardized and regulated industry that requires XML, schema validation and namespaces, and offer both JSON and XML unless the costs are staggering. Ideally, let consumers switch between formats using the HTTP Accept header, or by just changing an extension from .xml to .json on the URL.
Be aware that as soon as we start talking about XML support, we start talking about schemas for validation, namespaces, etc. Unless required by your industry, avoid supporting all that complexity initially, if ever. JSON is designed to be simple, terse and functional. Make your XML look like that if you can.
In other words, make the XML that is returned more JSON-like -- simple and easy to read, without the schema and namespace details present, just data and links. If it ends up being more complex that this, the cost of XML will be staggering. In my experience no one has used XML responses anyway for the last serveral years, it's just too expensive to consume.
(5) Create Fine-Grained Resources
When starting out, it's best to create APIs that mimic the underlying application domain or database architecture of your system. Eventually, you'll want aggregate services that utilize multiple underlying resources to reduce chattiness. However, it's much easier to create larger resources later from individual resources than it is to create fine-grained or individual resources from larger aggregates. Make it easy on yourself and start with small, easilly defined resources, providing CRUD functionality on those. You can create those use-case-oriented, chattiness-reducing resources later.
(6) Consider Connectedness
One of the principles of REST is connectedness -- via hypermedia links (search HATEOAS). While services are still useful without them, APIs become more self-descriptive and discoverable when links are returned in the response. At the very least, a 'self' link reference informs clients how the data was or can be retrieved. Additionally, utilize the HTTP Location header to contain a link on resource creation via POST (or PUT). For collections returned in a response that support pagination, 'first', 'last', 'next' and 'prev' links at a minimum are very helpful.
Regarding linking formats, there are many. The HTTP Web Linking Specification (
RFC5988) explains a link as follows:
a link is a typed connection between two resources that are identified by Internationalised Resource Identifiers (IRIs) [
RFC3987], and is comprised of:
- A context IRI.
- a link relation type
- a target IRI, and
- optionally, target attributes.
A link can be viewed as a statement of the form "{context IRI} has a {relation type} resource at {target IRI}, which has {target attributes}."
At the very least, place links in the HTTP Link header as recommended in the specification, or embrace a JSON representation of this HTTP link style (such as Atom-style links, see:
RFC4287) in your JSON representations. Later, you can layer in more complex linking styles such as
HAL+JSON,
Siren,
Collection+JSON, and/or
JSON-LD, etc. as your REST APIs become more mature.
3. Using HTTP Methods for RESTful Services
The HTTP verbs comprise a major portion of our "uniform interface" constraint and provide us the action counterpart to the noun-based resource. The primary or most-commonly-used HTTP verbs (or methods, as they are properly called) are POST, GET, PUT, PATCH, and DELETE. These correspond to create, read, update, and delete (or CRUD) operations, respectively. There are a number of other verbs, too, but are utilized less frequently. Of those less-frequent methods, OPTIONS and HEAD are used more often than others.
Below is a table summarizing recommended return values of the primary HTTP methods in combination with the resource URIs.
HTTP Verb | CRUD | Entire Collection (e.g. /customers) | Specific Item (e.g. /customers/{id}) |
---|---|---|---|
POST | Create | 201 (Created), ‘Location’ header with link to /customers/{id} containing new ID. | 404 (Not Found), 409 (Conflict) if resource already exists. |
GET | Read | 200 (OK), list of customers. Use pagination, sorting and filtering to navigate big lists. | 200 (OK), single customer. 404 (Not Found), if ID not found or invalid. |
PUT | Update/Replace | 405 (Method Not Allowed), unless you want to update/replace every resource in the entire collection. | 200 (OK) or 204 (No Content). 404 (Not Found), if ID not found or invalid. |
PATCH | Update/Modify | 405 (Method Not Allowed), unless you want to modify the collection itself. | 200 (OK) or 204 (No Content). 404 (Not Found), if ID not found or invalid. |
DELETE | Delete | 405 (Method Not Allowed), unless you want to delete the whole collection – not often desirable. | 200 (OK), 404 (Not Found), if ID not found or invalid. |
Below is a more-detailed discussion of the main HTTP methods. Click on a tab for more information about the desired HTTP method.
(1) POST:
The POST verb is most-often utilized to **create** new resources. In particular, it's used to create subordinate resources. That is, subordinate to some other (e.g. parent) resource. In other words, when creating a new resource, POST to the parent and the service takes care of associating the new resource with the parent, assigning an ID (new resource URI), etc.
On successful creation, return HTTP status 201, returning a Location header with a link to the newly-created resource with the 201 HTTP status.
POST is neither safe nor idempotent. It is therefore recommended for non-idempotent resource requests. Making two identical POST requests will most-likely result in two resources containing the same information.
Examples:
- POST http://www.example.com/customers
- POST http://www.example.com/customers/12345/orders
(2) GET:
The HTTP GET method is used to **read** (or retrieve) a representation of a resource. In the "happy" (or non-error) path. GET returns a representation in XML or JSON and an HTTP response code of 200 (OK). In an error case, it most often returns a 404 (NOT FOUND) or 400 (BAD REQUEST).
According to the design of the HTTP specification, GET (along with HEAD) requests are used only to read data and not change it. Therefore, when used this way, they are considered safe. That is, they can be called without risk of data modification or corruption-calling it once has the same effect as calling it 10 times, or none at all. Additionally, GET (and HEAD) is idempotent, which means that making multiple identical requests ends up having the same result as a single request.
Do not expose unsafe operations via GET-it should never modify any resources on the server.
Examples:
- GET http://www.example.com/customers/12345
- GET http://www.example.com/customers/12345/orders/
- GET http://www.example.com/buckets/sample
(3) PUT:
PUT is most-often utilized for **update** capabilities, PUT-ing to a known resource URI with the request body containing the newly-updated representation of the original resource.
However, PUT can also be used to create a resource in the case where the resource ID is chosen by the client instead of by the server. In other words, if the PUT is to a URI that contains the value of a non-existent resource ID. Again, the request body contains a resource representation. Many feel this is convoluted and confusing. Consequently, this method of creation should be used sparingly, if at all.
Alternatively, use POST to create new resources and provide the client-defined ID in the body representation-presumably to a URI that doesn't include the ID of the resource (see POST).
On successful update, return 200 (or 204 if not returning any content in the body) from a PUT. If using PUT for create, return HTTP status 201 on successful creation. A body in the response is optional-providing one consumes more bandwidth. It is not necessary to return a link via a Location header in the creation case since the client already set the resource ID.
PUT is not a safe operation, in that it modifies (or create) state on the server, but it is idempotent. In other words, if you create or update a resource using PUT and then make that same call again, the resource is still there and still has the same state as it did with the first call.
If, for instance, calling PUT on a resource increments a counter within the resource, the call is no longer idempotent. Sometimes that happens and it may be enough to document that the call is not idempotent. However, it's recommended to keep PUT requests idempotent.
Examples:
- PUT http://www.example.com/customers/12345
- PUT http://www.example.com/customers/12345/orders/98765
- PUT http://www.example.com/buckets/secret_stuff
(4) PATCH:
PATCH is used for **modify** capabilities. The PATCH request only needs to contain the changes to the resource, not the complete resource.
This resembles PUT, but the body contains a set of instructions describing how a resource currently residing on the server should be modified to produce a new version. This means that the PATCH body should not just be a modified part of the resource, but in some kind of patch language like JSON Patch or XML Patch.
PATCH is neither safe nor idempotent. However, a PATCH request can be issued in such a way as to be idempotent, which also helps prevent bad outcomes from collisions between two PATCH requests on the same resource in a similar time frame. Collisions from multiple PATCH requests may be more dangerous than PUT collisions because some patch formats need to operate from a known base-point or else they will corrupt the resource. Clients using this kind of patch application should use a conditional request such that the request will fail if the resource has been updated since the client last accessed the resource. For example, the client can use a strong ETag in an If-Match header on the PATCH request.
Examples:
- PATCH http://www.example.com/customers/12345
- PATCH http://www.example.com/customers/12345/orders/98765
- PATCH http://www.example.com/buckets/secret_stuff
(5) DELETE:
DELETE is pretty easy to understand. It is used to **delete** a resource identified by a URI.
On successful deletion, return HTTP status 200 (OK) along with a resource body, perhaps the representation of the deleted item (often demands too much bandwidth), or a wrapped response (see Return Values below). Either that or return HTTP status 204 (NO CONTENT) with no response body. In other words, a 204 status with no body, or the JSEND-style response and HTTP status 200 are the recommended responses.
HTTP-spec-wise, DELETE operations are idempotent. If you DELETE a resource, it's removed. Repeatedly calling DELETE on that resource ends up the same: the resource is gone. If calling DELETE say, decrements a counter (within the resource), the DELETE call is no longer idempotent. As mentioned previously, usage statistics and measurements may be updated while still considering the service idempotent as long as no resource data is changed. Using POST fro non-idempotent resource requests is recommended.
There is a caveat about DELETE idempotent, however. Calling DELETE on a resource a second time will often return a 404 (NOT FOUND) since it was already removed and therefore is no longer findable. This, by some opinions, makes DELETE operations no longer idempotent, however, the end-state of the resource is the same. Returning a 404 is acceptable and communicates accurately the status of the call.
Examples:
- DELETE http://www.example.com/customers/12345
- DELETE http://www.example.com/customers/12345/orders
- DELETE http://www.example.com/bucket/samples
4. Resource Naming
In addition to utilizing the HTTP verbs appropriately, resource naming is arguably the most debated and most important concept to grasp when creating an understandable, easily leveraged Web service API. When resources are named well, an API is intuitive and easy to use. Done poorly, that same API can feel klutzy and be difficult to use and understand. Below are a few tips to get you going when creating the resource URIs for your new API.
Essentially, a RESTful API ends up being simply a collection of URIs, HTTP calls to those URIs and some JSON and/or XML representations of resources, many of which will contain relational links. The RESTful principal of addressability is covered by the URIs. Each resource has its own address or URI -- every interesting piece of information the server can provide is exposed as a resource. The constraint of uniform interface is partially addressed by the combination of URIs and HTTP verbs, and using them in line with the standards and conventions.
In deciding what resources are within your system, name them as nouns as opposed to verbs or actions. In other words, a RESTful URI should refer to a resource that is a thing instead of referring to an action. Nouns have properties as verbs do not, just another distinguishing factor.
Some exampl resources are:
- Users of the system.
- Courses in which a student is enrolled.
- A user's timeline of posts.
- The users that follow another user.
- An article about horseback riding.
Each resource in a service suite will have at least one URI identifying it. And it's best when that URI makes sense and adequately describes the resource. URIs should follow a predictable, hierarchical structure to enhance understandability and, therefore, usability: predictable in the sense that they're consistent, hierarchical in the sense that data has structure -- relationships. This is not a REST rule or constaint, but it enhances the API.
RESTful APIs are written for consumers. The name and structure of URIs should convey meaning to those consumers. It's often difficult to know what the data boundaries should be, but with understanding of your data, you most-likely are equipped to take a stab and what makes sense to return as a representation to your clients. Design for your clients, not for your data.
Let's say we're describing an order system with customers, orders, line items, products, etc. Consider the URIs involved in describing the resources in this service suite:
(1) Resource URI Examples:
To insert (create) a new customer in the system, we might use: "POST http://www.example.com/customers".
To read a customer with Customer ID#33245: "GET http://www.example.com/customers/33245". The same URI would be used for PUT and DELETE, to update and delete, respectively.
Here are proposed URIs for products: "POST http://www.example.com/products" for creating a new product. "GET|PUT|DELETE http://www.example.com/products/66432" for reading, updating, deleting product 66432, respectively.
Now, here is where it gets fun... What about creating a new order for a customer? One option might be: "POST http://www.example.com/orders". And that could work to create an order, but it's arguably outside the context of a customer.
Because we want to create an order for a customer (note the relationship), this URI perhaps is not as intuitive as it could be. It could be argued that the following URI would offer better clarity: "POST http://www.example.com/customers/33245/orders". Now we know we're creating an order for customer ID#33245.
Now what would the following return? "GET http://www.example.com/customers/33245/orders". Probably a list of orders that customer #33245 has created or owns. Note: we may choose to not support DELETE or PUT for that url since it's operating on a collection.
Now, to continue the hierarchical concept, what about the following URI? "POST http://www.example.com/customers/33245/orders/8769/lineitems". That might add a line item to order #8769 (which is for customer #33245). Right GET for that URI might return all the line items for that order. However, if line items don't make sense only in customer context or also make sense outside the context of a customer, we would offer a "POST www.example.com/orders/8769/lineitems" URI.
Along those lines, because there may be multiple URIs for a given resource, we might also offer a "GET http://www.example.com/orders/8769" URI that supports retrieving an order by number without having to know the customer number.
To go one layer deeper in the hierarchy: "GET http://www.example.com/customers/33245/orders/8769/lineitems/1". Might return only the first line item in that same order.
By now you can see how the hierarchy concept works. There aren't any hard and fast rules, only make sure the imposed structure makes sense to consumers of your services. As with everything in the craft of Software Development, naming is critical to success.
Look at some widely used APIs to get the hang of this and leverage the intuition of your teammates to refine your API resource URIs. Some example APIs are:
- Twitter: https://developer.twitter.com/en/docs/api-reference-index
- Facebook: https://developers.facebook.com/docs/reference/api/
- LinkedIn: https://developer.linkedin.com/apis
(2) Resource Naming Anti-Patterns:
While we've discussed some example of appropriate resource names, sometimes it's informative to see some anti-patterns. Below are some examples of poor RESTful resource URIs seen in the "wild". These are examples of what not to do!
First up, often services use a single URI to specify the service interface, using query-string parameters to specify the requested operation and/or HTTP verb. For example to update customer with ID 12345, the request for a JSON body might be: "GET http://api.example.com/services?op=update_customer&id=12345&format=json".
By now, you're above doing this. Even though the 'services' URL node is a noun, this URL is not self-descriptive as the URI hierarchy is the same for all requests. Plus, it uses GET as the HTTP verb even though we're performing an update. This is counter-intuitive and is painful (even dangerous) to use as a client.
Here's another example following the same operation of updating a customer: "GET http://api.example.com/pdate_customer/12345". And its evil twin: "GET http://api.example.com/customers/12345/update".
You'll see this one a lot as you visit other developer's service suites. Note that the developer is attempting to create RESTful resource names and has made some progress. But you're better than this -- able to identify the verb phrase in the URL. Notice that we don't need to use the 'update' verb phrase in the URL because we can rely on the HTTP verb to inform that operation. Just to clarify, the following resource URL is redundant: "PUT http://api.example.com/customers/12345/update".
With both PUT and 'update' in the request, we're offering to confuse our service consumers! Is 'update' the resource? So, we've spent some time beating the horse at this point. I'm certain you understand...
(3) Pluralization:
Let's talk about the debate between the pluralizers and the "singularizers"... Haven't heard of that debate? It does exists. Essentially, it boils down to this question...
Should URI nodes in your hierarchy be named using singular or plural nouns? For example, should your URI for retrieving a representation of a customer resource look like this: "GET http://www.example.com/customer/33245", or "GET http://www.example.com/customers/33245".
There are good arguments on both sides, but the commonly-accepted practice is to always use plurals in node names to keep your API URIs consistent across all HTTP methods. The reasoning is based on the concept that customers are a collection within the service suite and the ID (e.g. 33245) refers to one of those customers in the collection.
Using this rule, an example multi-node URI using pluralization would look like (emphasis added): "GET http://www.example.com/customers/33245/orders/8769/lineitems/1", with 'customers', 'orders', and 'lineitems' URI nodes all being their plural forms.
This implies that you only really need two base URLs for each root resoruce. One for creation of the resource within a collection and the second for reading, updating and deleting the resource by its identifier. For example the creation case, using customers as the example, is handled by the following URL: "POST http://www.example.com/customers". And the read, update and delete cases are handled by the following: "GET|PUT|DELETE http://www.example.com/customers/{id}".
As mentioned earlier, there may be multiple URIs for a given resource, but as a minimum full CRUD capabilities are aptly handled with two simple URIs.
You ask if there is a case where pluralization doesn't make sense. Well, yes, in fact there is. When there isn't a collection concept in play. In other words, it's acceptable to use a singularized resource name when there can only be one of the resource -- it's a singularized noun to represent that: "GET|PUT|DELETE http://www.example.com/configuration".
Note the lack of a configuration ID and usage of POST verb. And say that there was only one configuration per customer, then the URL might be: "GET|PUT|DELETE http://www.example.com/customers/12345/configuration".
Again, no ID for the configuration and no POST verb usage. Although, i'm sure that in both of these cases POST usage might be argued to be valid. Well... OK.
5. Idempotence
Idempotence is a funky word that often hooks people. Idempotence is sometimes a confusing concept, at least from the academic definition.
From a RESTful service standpoint, for an operation (or service call) to be idempotent, clients can make that same call repeatedly while producing the same result. In other words, making multiple identical requests has the same effect as making a single request. Note that while idempotent operations produce the same result on the server (no side effects), the response itself may not be the same (e.g. a resource's state may change between requests).
The PUT and DELETE methods are defined to be idempotent. However, there is a caveat on DELETE. The problem with DELETE, which if successful would normally return a 200 (OK) or 204 (No Content), will often return a 404 (Not Found) on subsequent calls, unless the service is configured to "mark" resources for deletion without actually deleting them. However, when the service actually deletes the resource, the next call will not find the resource to delete it and return a 404. However, the state on the server is the same after each DELETE call, but the response is different.
GET, HEAD, OPTIONS and TRACE methods are defined as safe, meaning they are only intended for retrieving data. This makes them idempotent as well since multiple, identical requests will behave the same.