This is the multi-page printable view of this section.
Click here to print.
Return to the regular view of this page.
Concepts
Concepts of REST.
REST Protocol: HTTP
REST is based on the HTTP protocol with all its advantages and disadvantages. The reason for this was that HTTP was already a de facto standard for content network communication at that time and maximum server and client compatibility was a high priority.
HTTP is also technology-independent, so that data can be transferred securely and validly even between different systems, runtimes and frameworks.
REST Structure: URI / URL Design
HTTP URLs are the main point of the REST API design. The structure of URLs therefore have a scheme that you should follow, if you want to develop a good HTTP API.
URLs are also what is visible first later - for example in documentations - and thus can lead to a first judgment about an API quality. The better and more standardized the structure of the URLs, the better the API will be received by developers.
See more: URL Design
REST Response: Plaintext
REST has no real rule about what kind of format is transmitted as long as the content is based on plaintext: the developer decides whether XML, Json, or some other format is returned. Many APIs also support via HTTP Header (Content-Type
) multiple formats for content transfer, e.g. Json and XML.
See more: REST Formats
REST Benefits
- HTTP makes REST very scalable and bullet-proof.
- Since REST is based on HTTP there is a very high compatibility between technologies.
- There are clear rules for server and client that can be followed and cover even complex scenarios.
See more: REST Alternatives
1 - URL Design
URL best practises to design resource identifiers
HTTP URLs are the main point of the REST API design. The structure of URLs therefore have a scheme that you should follow, if you want to develop a good HTTP API.
URLs are also what is visible first later - for example in documentations - and thus can lead to a first judgment about an API quality. The better and more standardized the structure of the URLs, the better the API will be received by developers.
The creation of a URL design is one of the most important foundations of an API. This should be implemented very carefully as part of an architectural approach.
Every change has a direct impact on the clients and represents a breaking change.
Naming, Collections and Nouns
The purpose of a URI is to uniquely address resources. Accordingly, it is the common way and recommendation that the URI sections are identifiers of resources. The structure should therefore be designed in such a way that even a human can navigate through the data.
So if you want to see what tasks there are, the base would be /tasks
.
The API would then respond with a list of tasks.
// URL: /tasks
[
{
"id": 123,
"title": "Buy flowers",
"isDone" : false
},
{
"id": 789,
"title": "Water flowers",
"isDone" : false
},
... more tasks here
]
The list of resources (here tasks) should always contain the identifier of the respective object, here the Id.
The Id is now used to uniquely identify the individual address via the URI, here for example /tasks/123
.
// URL: /tasks/123
{
"id": 123,
"title": "Buy flowers",
"isDone" : false
}
Actions are HTTP Verbs
Verbs represent actions on resources that are implemented in REST via HTTP Request Methods.
HTTP Verb |
CRUD |
Examples |
Action description |
Behavior |
|
GET |
Read |
/tasks , /tasks/123 |
Query an entire collection or an individual resource |
If a collection or a resource is found, the response is given together with status code 200 (Ok), otherwise 404 (Not found). |
|
POST |
Create |
/tasks |
Passes a new resource to a collection |
Often status code 200 (Ok) is returned with the create resource as body. Asynchronous APIs often respond with status code 201 (Created) and the id of the created resource or an operation. If the collection does not exist, the answer is 404 (Not found). |
|
PUT |
Update/Replace |
/tasks/123 |
Replaces an existing resource |
The updated resource is used as the response body together with status 200 (Ok). If the resource does not exist, the response is given with status code 404 (Not found). If a resource cannot be updated, the response is 409 (Conflict) or status code 405 (Not Allowed) if updating is not allowed. |
|
PATCH |
Update/Modify |
/tasks/123 |
Updates an existing or a part of an existing resource |
The updated resource is used as the response body together with status 200 (Ok). If the resource does not exist, the response is given with status code 404 (Not found). If a resource cannot be updated, the response is 409 (Conflict) or status code 405 (Not Allowed) if updating is not allowed. |
|
DELETE |
Delete |
/tasks/123 |
Deletes an existing resource |
The deleted resource is used as the response body together with status 200 (Ok). If the resource does not exist, the response is made with status code 404 (Not found). If a resource cannot be deleted, the response is 409 (Conflict) or with status code 405 (Not Allowed) if deletion is not allowed. |
|
Due to the different verbs, a single URI endpoint is able to offer different actions, respectively different returns.
- POST on
/tasks
adds a new task to the collection.
- GET on
/tasks
returns all existing tasks in the collection.
It is therefore extremely important to implement and use the verbs correctly.
Hierarchical objects
Usually resources are not as simple as in this task example, but have relations to other resources or have further objects as part of their hierarchy.
So let’s assume that instead of the task we have an article, it might look like this:
// URL: /article/123
{
"id": 123,
"createdOn" : "2022-01-27T108:27+03:00",
"modifiedOn" : "2022-02-03T16:47+18:00",
"title": "REST is nice! Use it now in your public HTTP API!",
"text" : "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet."
"textType" : "PlainText",
"author" : {
"id" : 3105,
"name: "Benjamin Abt",
"mail" : "mail@benjamin-abt.com",
"nickname" : "Ben",
"twitter" : {
"url" : "https://twitter.com/abt_benjamin",
"username" : "abt_benjamin"
"avatarUrl" : "https://pbs.twimg.com/profile_images/1404387914344243203/oUO_LNAp_400x400.jpg"
}
},
"tags" : ['.NET','Azure','API','REST'],
"comments" : [{
"id" : 8758,
"text" : "orem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam",
"on" : "2022-01-29T16:47+01:00",
"by" : {
"id" : 789,
"name" : "Batman"
}
},{
"id" : 485,
"text" : "orem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam",
"on" : "2022-01-30T16:47+01:00",
"by" : {
"id" : 325,
"name" : "Robin"
}
},{
"id" : 63789,
"text" : "orem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam",
"on" : "2022-01-29T16:47+01:00",
"by" : {
"id" : 245,
"name" : "Catwoman"
}
}]
}
However, it should be noted:
- The hierarchical structure is now given by the embedded objects like author or even comments, which in turn have their own hierarchical structure.
- There is no fixed rule that this structure is directly addressable by the URL, but it is quite common. However, the resources themselves should be individually addressable. The hierarchical structure of the author from the point of view of the article is:
/articles/123/author
. However, the author should also be addressable with /authors/3105
.
- The id results from the hierarchical object (in article), which is why the id always should be part of a resource. However, the hierarchical resource return (
/articles/123/author
) may be different from the identifier url resource return (/authors/3105
).
Filtering
Filtering of returns should always be part of an API. However, REST does not have a standard at this point.
However, it is common to be able to filter based on properties of the resource. For example, based on the id.
/articles?id=123
Filtering
Pagination is especially in request when dealing with very large lists, or when an API is not supposed to give out all the data at once. The usual identifiers for the limited returns are skip
and take
, or limit
and offset
.
Query |
Result |
/articles?take=10 |
returns the first 10 articles (index 0 to 9) |
/articles?skip=20 |
skips the first 20 articles (index 0 to 19) and returns the article collection starting from index 20 |
/articles?take=10skip=20 |
skips the first 20 articles (index 0 to 19) and returns 10 starting from index 20 |
As can be seen, the take parameter in this case is Optional, which requires that the server knows a default (e.g. 10) if the client does not transmit a value at this point. The alternative would be to declare this value as mandatory. However, besides a default, it is also important to limit the values themselves, so that, for example, a maximum of 100 elements are returned, no matter what the client specifies.
Additionally, it is also important to note that the client itself must know how many resources are in a collection in order to be able to calculate the number of pages. This value can be transmitted either via the header, or as part of the body. In the second case, the general structure of the response must be modified: we have to add a metadata object.
{
"data" : [ // collection of articles
{
"id": 123,
"createdOn" : "2022-01-27T108:27+03:00",
"modifiedOn" : "2022-02-03T16:47+18:00",
"title": "REST is nice! Use it now in your public HTTP API!",
"...."
},
{
"id": 456,
"createdOn" : "2022-01-27T108:27+03:00",
"modifiedOn" : "2022-02-03T16:47+18:00",
"title": ".NET is a great platform to use!",
"...."
}
],
"metadata" {
"take" : 10, // articles to take
"skip": 20, // articles to skip
"total" : 738 // total numbers of articles, so we know we have 74 pages each 10 articles in total (last page 8)
}
Security
When it comes to security, special care must be taken to ensure that no information bypasses the HTTPS encryption. This means that all sensitive content must be transmitted either via HTTP headers or via the HTTP body. The query values are not part of the encryption and can be seen from any point in the data transmission despite HTTPS!
See Security for details.
Versioning
Versioning is always a topic of discussion, since there is not only one way here either.
The general possibilities are:
- HTTP URI (
/api/v2/articles/...
)
- HTTP query parameter values (
/articles?version=2
)
- HTTP header values
There are pros and cons to all three, but by far the most common and general recommended way is to version via the URL.
See Versioning for details.
2 - REST Format
REST Formats
REST has no real rule about what kind of format is transmitted as long as the content is based on plaintext: the developer decides whether XML, Json, or some other format is returned. Many APIs also support via HTTP Header (Content-Type) multiple formats for content transfer, e.g. Json and XML.
Why XML
Although XML has a significant overhead, unlike Json, XML can be validated extremely well and easily. XML also has a much more extensive feature set, such as comments.
Why Json
Json has clear disadvantages in terms of validation, but has a very low overhead and thus reduces bandwidth and response times. However, Json continues to evolve, so perhaps comments and other features will be supported soon.
Why YAML
There is no reason to use YAML as API format.
YAML is hell.
Please don’t use YAML!
3 - Maturity Levels
The path to adulthood of an API
Not every API is also a REST API. Until an API really becomes a REST API - Martin Fowler has called this the “Glory of REST” or also “Steps towards REST” - different levels have to be reached.
The levels (Level 0 to Level 3) originate from Leonard Richardson, who divided the respective principles into the four levels known today.
It should be said that very few APIs actually fully achieve the rules of all four levels. Especially the last level - Hypermedia - is reached by almost no API. Very rare to see this.
Level 2 is considered the most common designation of REST today.
Why those levels?
In principle, you would not need these levels. However, they help enormously in both the architecture and implementation phases to ensure that recognizable mechanisms are used, thus creating a common basis for both tooling and developers.
Even if other APIs may not follow these levels, it makes sense for your API.
Your users will thank you for it.
Level 0 - Thanks for interfacing at all
Level 0 is still very common today, if not the most common level at all.
This level does not describe anything other than communication: HTTP is used as the basic channel. No more, and no less.
Especially for very simple APIs in the professional environment, APIs are implemented in the so-called RPC style: there is one flat URL per action, like /task/createTask
, /task/deleteTaskById
.
This is not a bad thing across the board: alternatives to REST (like gRPC) still follow this style today and there are valid use cases for it. But this is not a REST API.
Level 1 - Urls and Resources
Resources, or entities, are the basic building block of any REST API. Level 1 describes how URLs and the various resources are handled in an API or in an application. Thereby each resource gets its own endpoint.
Resource |
Endpoint |
Users |
/users |
Tasks |
/tasks |
Producs |
/products |
However, this does not only apply to the endpoints of an entire collection, but also to the respective resource, for example /users/ben
. If a resource also has sub-resources, i.e. a nested structure, level 1 also describes the access to this resource through the URL structure. For the following example the responsible person of a task is reachable via /task/123/assginee
{
"id" : 123,
"title": "This is the task title",
"assignee" : {
"id": 315,
"name: "Batman"
}
}
Level 2 - This is REST today
Method |
Description |
GET |
GET is used to retrive an entity or a collection of entities of an endpoint (URI). |
POST |
POST is used to pass an entity to an endpoint (URI). It is used to create an entity or modify a state. |
PUT |
PUT also passes an entity to an endpoint, but ensures that an existing entity on the server is completely overwritten. |
PATCH |
PATCH is the possibility to change an existing resource on the server only partially. |
DELETE |
DELETE deletes an existing entity on the server |
Level 3 - The unreachable throne