REST Good Practices: Beyond the Code
It's important to understand REST best practices when implementing microservices to build REST APIs that enable modular architecture.
Join the DZone community and get the full member experience.Join For Free
When we start a project today and adopt modular composition, it is common to consider the use of microservices, either by separating the business model from the visualization layer or by decomposing the context into smaller and theoretically easier parts to implement and maintain, while still integrating more easily with CI/CD tools such as Jenkins and CircleCI. Some other reasons to do so are in this DZone article.
Figure 1. Possibilities of microservices adoption.
When using microservices, we will most likely be departing from an architectural style using the REST protocol. This concept is well known and widely used (if you have never heard of REST, you may want to see this link), but the question of this article is whether we are actually designing our REST architectures correctly, taking some care that goes beyond just creating endpoints and manipulating data (things that sometimes we do not pay attention to). Below are some points that I consider important to REST API design:
This point, while being the most subjective in a REST API, is undoubtedly the most important. When developing an API for a consumer (whoever it may be), remember to meet your customer's needs: produce what they expect, spend effort developing features that will actually be used, and make improvements that add value to what is being produced, keeping your API cohesive and easy to understand. Invest time in documentation. Realize that you are doing something to be used by others, not by you (in most cases).
Nouns > Verbs
Using nouns as resource identifiers in the URI of a REST endpoint is considered a good practice, making reading simple and standardized. Let the HTTP verbs take care of the "type" of operation that will be performed on the resource (such as GET, POST, or DELETE). By designing your endpoints in this way, the client will not need to know all the names of your API resources, just the main nouns and HTTP verbs that each feature supports.
Figure 2. Example of API endpoints and HTTP verbs.
Working with HTTP verbs includes using them in the correct way together with the statuses that contain the operations. There are currently more than 70 existing HTTP statuses, each in order to make the behavior of an API more readable, either in the successful access of a resource/operation (200), the creation of a new resource (201), or to inform that something unexpected happened on the server (500). Because there are so many, it is impossible to remember all of the statuses, but when designing an API, I believe that at least 7 to 10 statuses are commonly used, such as
200 - OK - Everything is working.
201 - OK - A new feature was created.
204 - OK - A resource was successfully deleted.
304 - Unmodified - The client can use cached data.
400 - Invalid request - the request was invalid or could not be displayed. The exact error must be explained in the error load, for example, "JSON is not valid."
401 - Unauthorized - The request requires user authentication.
403 - Forbidden - The server understands the request but is declining or access is not allowed.
404 - Not Found - There is no resource behind the URI.
422 - Unprocessable entity - must be used if the server can not process the property, for example, if an image cannot be formatted or if required fields are missing from the payload.
500 - Internal server error - API developers should avoid this error. If such an error occurs, stracktrace must be registered and not returned in the response.
Classification, Filtering, Search, and Paging
These items, however common they seem, are still few in REST APIs. All of these operations can (and in some cases, should) be performed over a single set of data (e.g. people).
In case, the client wants to get the sorted list of people, the GET / people terminal must accept several ranking parameters in the query.
For example, GET / people? Sort = name_desc would sort people alphabetically (descending).
To filter the dataset, we can pass several options through query parameters.
For example, GET / people? Type = pf & location = SC would filter the data from the list of physical persons of Santa Catarina(Brazil State).
When searching for the name of the person in the list of people, the endpoint to be exported should be of type GET = / people?Search=fernando
When the dataset is too large, we divide the dataset into smaller pieces, which helps improve performance and is easier to handle the response (control items per page and how many pages can be shown).
For example: GET / people? Page = 23 means getting the list of people from the 23rd page.
P.S: If you add many query parameters in GET methods to make the URI too long, the server may respond with a very long 414 HTTP Status URI. In these cases, params can also be passed in the body of the POST method request.
Good API design is a critical component so that its use is not only functional but also self-explanatory and concise. The consistency of the API must remain for a long time, regardless of changes of technologies and implementations of the same. As a suggestion, you could suggest creating use cases to test your API, creating consumer clients that would validate the items presented above (and as many that can be found in the references). If you work with APIs, either by consuming or producing them, and have something to add, please comment below
Opinions expressed by DZone contributors are their own.