Design: 13 Best Practices to Make Your Users Happy

Writing in the front

The reason why I translate this article is because since I became a front-end code farmer, it has become a common thing to tune the interface, and it is accompanied by countless debates and frustration. Writing friendly restful apis is critical not only for your colleagues but also for your future users as third-party service invocation interfaces. I have read many articles and books about restful apis and design principles. After reading the original text, I found the 13 best practices in the article to be comprehensive and instructive, so I translated them and shared them with you. If there are mistakes, but also hope to correct.

As I generally tend to free translation, I will omit the opening words or some irrelevant content in the original text. After all, time is money. If you are good at English and can surf the Internet scientifically, I suggest you read the original text, so as not to cause errors in understanding.

1. Understand HTTP as it applies to REST

If you want to build well-designed REST apis, it’s helpful to know some of the basics of the HTTP protocol.

There is a lot of good documentation on HTTP on MDN. However, in terms of the REST API design itself, the HTTP knowledge points involved probably include the following:

  • Contained in the HTTPThe verb(or method) :GET,POST,PUT,PATCHThere areDELETEIs the most commonly used.
  • REST isResource orientedA resource is a resourceURIIdentified by, for example/articles/.
  • Endpoint“Is a combination of a verb and a URI, for exampleGET: /articles/.
  • An endpoint can be interpreted as some action on some resource. For instance,POST: /articlesMay stand for “Create a new article”.
  • In the business world, we can often combineThe verbandCRUD(add, delete, check, modify)GETOn behalf of the check,POSTOn behalf of,PUTPATCH(Note: PUT usually stands for overall update, while PATCH stands for local update), andDELETEOn behalf of the delete.

Of course, you can apply anything that the HTTP protocol provides to REST API design, but these are fairly basic, so it’s important to keep them in mind at all times.

2. Do not return plain text

Although data returned in JSON data format is not mandated by the REST architecture specification, most REST apis follow this guideline.

However, it’s not enough to just return JSON data. You also need to specify a header that returns the body, such as Content-Type, whose value must be Application/JSON. This is especially important for programmatic clients (such as those interacting with the API through Python’s Requests module) — whether or not these programs decode the returned data properly depends on the header.

Note: In general, this does not seem to be a problem for browsers, which generally have built-in content sniffing mechanisms, but it is better to set this header in the response for consistency.

3. Avoid verbs in URIs

If you understand the message of best practice 1, you now know that you should not put verbs in URIs of REST apis. This is because HTTP verbs are sufficient to describe business logic operations performed on resources.

For example, if you want to provide an interface that provides a banner image for an article and returns it, you might implement an interface in the following format:

GET: /articles/:slug/generateBanner/
Copy the code

This interface is a read interface, so it can be simplified as:

GET: /articles/:slug/banner/
Copy the code

Similarly, if this port is to create an article:

Don't do this POST: / / / articles/createNewArticle POST: / / / this is the best practice/articles /Copy the code

Try using HTTP verbs to describe the business logic operations involved.

4. Use plural nouns to describe resources

Sometimes there is confusion over whether to use the plural or singular form of a resource, such as /article/:id/ or /articles/:id/?

I recommend the latter here. Why is that? Because the complex form can satisfy all types of endpoints.

The singular GET /article/2/ looks good, but what about GET /article/? Can you tell if the interface returns an article or multiple articles just by the literal?

Therefore, to avoid ambiguity caused by singular names and to be as consistent as possible, use plural forms, such as:

GET: /articles/2/
POST: /articles/
...
Copy the code

5. Return error details in the response

When an API server handles an error, the ability to include the error message in the RETURNED JSON body can help the interface caller to some extent with debugging. For example, for a common submission form, you get the following error message:

{
    "error": "Invalid payoad."."detail": {
        "surname": "This field is required."}}Copy the code

Interface callers quickly understand why the error occurred.

Be careful with the Status code

This is probably the most important, most important, most important point, and probably the only one you need to remember in this article.

As you probably know, you can return an error response with a 200 status code in HTTP, but that’s pretty bad. Instead, you should return a meaningful status code consistent with the type of error returned.

An intelligent reader might say, couldn’t I follow best practice no. 5 to provide enough detail? Sure, but let me tell you a story:

I have used an API that returns a status code of 200 OK for all responses and uses the status field in the response data to indicate whether the current request was successful, such as:

{
    "status": "success"."data": {}}Copy the code

So, although the status code is 200 OK, I’m not absolutely sure that the request was successful, and in fact, when an error occurs, the API returns a response as follows:

HTTP/1.1 200 OK Content-type: text/ HTML {"status": "failure"."data": {
        "error": "Expected at least two items in list."}}Copy the code

The header is still text/ HTML because it also returns some HTML fragments.

Because of this, I have to check the correctness of the response status code and also verify the value of the status field with special meaning, so that I can safely process the data returned by the response.

One of the real downsides of this design is that it breaks the “trust” between the interface and the caller, because you might be worried that the interface is lying to you (note: the implication is that since AD hoc fields can change, this increases unreliability).

So, use the correct status code, return an error message only in the body of the response, and set the correct header, such as:

HTTP/1.1 400 Bad Request Content-Type: Application /json {"error": "Expected at least two items in list."
}
Copy the code

7. Keep the status code consistent

Once you have mastered the proper use of status codes, you should strive to make them consistent.

For example, if a POST endpoint returns 201 Created, all POST endpoints should return the same status code. The advantage of this is that the caller does not have to care that the status code returned by the endpoint depends on some particular condition, and consistency is achieved. If there are special cases, state them prominently in the document.

Here are my recommended status codes for verbs:

GET: 200 OK
POST: 201 Created
PUT: 200 OK
PATCH: 200 OK
DELETE: 204 No Content
Copy the code

8. Do not nest resources

Retrieving resource data using REST apis usually involves directly retrieving multiple or single resources, but what do we do when we need to retrieve related resources?

For example, we expect to get an article list with an author — assuming authro’s ID =12. There are two options:

The first scenario is described by placing nested resources after associated resources in urIs, for example:

GET: /authors/12/articles/
Copy the code

The reason some people recommend this approach is that this form of URI partly describes a one-to-many relationship between author and article. At the same time, however, combined with best practice # 4, it is not clear whether the data returned by the current endpoint is of type Author or article type.

Here’s an article detailing how flat forms are better than nested forms, so there must be a better way, which is the second option below:

GET: /articles/? author_id=12Copy the code

Simply remove the logic to filter article to queryString. This URI more clearly describes the meaning of “get article with all authors (id=12)” than before.

9. Handle trailing slashes gracefully

Whether or not a good URI should include trailing slashes is modical, just choose a more inclined style and be consistent, and provide a redirection response when a client misuses trailing slashes.

Let me tell you one more story of my own. The other day, I was integrating an API endpoint into the project, but I kept getting 500 Internal errors, and the endpoint I called looked something like this:

POST: /entities
Copy the code

After debugging for a while, I almost broke down because I didn’t know what I was doing wrong until I realized that the server had reported 500 because I had carelessly dropped a trailing slash. When I changed my URI to:

POST: /entities/
Copy the code

After that, everything worked fine.

Of course, most Web frameworks gracefully handle and provide customization options for urls that contain trailing slashes, so if you can, find it and turn it on.

10. Use QueryString for filtering and paging

In most cases, a simple endpoint does not fit the responsible business scenario.

Your users may want to retrieve certain sets of data under certain conditions, and to ensure performance, only a subset of that set. In other words, this is usually called filtering and paging:

  • Filtering: The user can provide additional properties to control the set of data returned
  • Paging: Gets a subset of the data set. The simplest paging is based on the number of pagespagepage_sizeTo determine the

So the question is, how do we combine these two features with RESTful apis?

The answer, of course, is queryString. For paging, this is obviously the best way to do it, for example:

GET: /articles/? page=1&page_size=10Copy the code

But with filtering, you might commit the same problems as best practice # 8, such as getting a published article list:

GET: /articles/published/
Copy the code

In addition to the previous questions, there is a design issue here, which is that published itself is not a resource, it is simply a feature of the resource. Feature fields like this should be placed in queryString:

GET: /articles/? published=true&page=2&page_size=20
Copy the code

More elegant and clear, isn’t it?

11. Distinguish between 401 and 403

When we encounter an API error about security, it’s easy to confuse the two different types of errors, authentication and authorization (such as permission-related) — and to be honest, I often get confused myself.

Here’s my own summary of the memo, which explains how I can, in practice, distinguish between them:

  • Does the user not provide authentication credentials? Is the certification still valid? This type of error is generally unauthenticated (401 Unauthorized).
  • Is the user properly authenticated but does not have the required permissions to access the resource? This is generally not authorized (403 Forbidden)

A. Accepted B. Accepted C. Accepted D. Accepted

I find that 202 Accepted is a very convenient alternative to 201 Created in some cases, and this status code means:

The server has accepted your request, but no new resources have been created so far, but everything is fine.

I share two business scenarios that are particularly suitable for using 202 Accepted status codes:

  • If the resource is created after a future sequence of processes, such as when a job is completed
  • If the resource already exists, but this is the ideal state and therefore should not be identified as an error

13. Use REST API customization framework

As a final best practice, let’s explore the question: How do you practice best practices in API implementation?

Often, you want to quickly create an API so that services can access each other. Python developers might immediately pull out Flask, and JS developers, not to be outdone, might opt for Express, which implements some simple routes to handle HTTP requests.

The problem with this is that, in general, Web frameworks do not exist specifically for building REST API services; in other words, Flask and Express are two very generic frameworks, but they are not particularly suited for building REST API services. So you have to take extra steps to implement best practices in your API, but most of the time, laziness or time constraints mean you don’t put too much effort into them — and then give your users a weird API endpoint.

The solution is simple: if you want to do a good job, you must first sharpen your tools, and mastering and using the right work is the best solution. In various languages, a number of new frameworks have emerged specifically for building REST API services that can help you get things done easily, while following best practices, without sacrificing productivity. One of the best API frameworks I’ve found in Python is Falcon. It’s as simple as Flask, very efficient, and ideal for building REST API services. If you prefer Django, the Django REST Framework will suffice. It’s not intuitive, but it’s very powerful. Restify also seems like a good option on NodeJS, although I haven’t tried it yet. I strongly encourage you to give these frameworks a chance! They will help you build well-regulated, elegant, and well-designed REST API services.

conclusion

We should all aim to make calling an API fun. Hopefully, this article has provided you with some tips and tricks for building better REST API services. To me, these best practices boil down to three things: good semantics, simplicity, and rationality.

Pay attention to the public account full stack 101, only talk about technology, not life