Friends who are not familiar with RESR can also read

Whether you are a front-end developer, you will encounter API design more or less in the process of programming, but it is not easy to design an easy-to-use and maintainable API (of course not everyone feels this way). When creating an API from scratch, you need to get a lot of details right and think about them. From basic security concerns to using the correct HTTP methods, authentication, deciding which requests and responses you should accept and return, and much more… Too many to list.


So today I’m going to do my best to condense everything I know about what makes a good API and share it with you (not everyone, of course, so I’ll just share my opinion). This article is independent of the programming language used, so they are applicable to any framework or technology.








1. Use ISO 8601 UTC dates

When dealing with dates and times, the API recommends always returning strings in ISO 8601 format.

Client-side applications are important for displaying the correct date in a particular time zone when used, and the data is easy to process.

{
    "article_writing_time": "The 2022-03-07 T14:30:54. 149 z"
}
Copy the code





2. Develop high availability plans for important APIS

For some important apis, such as shopping cart billing, we can’t debug one problem at a time until customer service loses interest.

So I recommend providing a deterministic service for apis such as GET/Health to determine whether they are healthy or not. Other applications, such as load balancers, can invoke this endpoint to take action in the event of a service interruption.





3. Accept API key authentication

If the API needs to be called by a third party, authentication through the API key makes sense.

API keys should be passed using custom HTTP headers such as apI-key. They should have an expiration date, and the active keys must be revocable so that they can be invalidated if they are compromised. Avoid checking API keys into source code (similar to environment variables).





4. Use a reasonable HTTP approach

There are many HTTP methods, but the most important are:

  • POSTUsed to create resources
    • POST /user
  • GETFor reading resources (individual resources and collections)
    • GET /user
    • GET /user/{id}
  • PATCHUsed to apply partial updates to a resource
    • PATCH /user/{id}
  • PUTUsed to apply full updates to a resource (replacing the current resource)
    • PUT /user/{id}
  • DELETEUsed to delete a resource
    • DELETE /user/{id}





5. Use standardized error responses

In addition to using HTTP status codes that indicate the result of the request (success or error), always use a standardized error response with more detailed information about the problem when an error is returned.

The user who calls the API always wants to return the same structure every time and can easily act accordingly.

// Request => GET /user/uiuing.com

// Response <= 404 Not Found
{
    "code": "/user/not_found"."message": "Can't find user with ID uiuing.com!"
}
Copy the code
// Request => POST /register
{
	"name":"uiu"."webSite":"uiuing.com"."nickname":"I want a cat."
}

// Response <= 400 Bad Request
{
    "code": "/register/password_required"","message":"The [password] parameter is required!"}Copy the code





6, use,PATCHInstead ofPUT

As mentioned earlier, PATCH requests should apply partial updates to resources, while PUT completely replaces existing resources.

  • It is also dangerous to allow any field to be updated without any restrictions;

I suggest to design the request around PATCH, because:

  • When usingPUTUpdating only a portion of a resource’s fields still requires passing the entire resource, making it more network intensive and error-prone;
  • In my experience, there are almost no use cases in practice that make sense for a complete update of a resource.





7. Use paging

All returned resource sets are combined and the requests with the same response structure are paginated. Such as page size (or similar) to control the blocks to be retrieved.

// Request => GET /home/v1/get-business-list? page=1&size=10&username=uiu

// Response <= 200 OK
{
	"code":200."message":"success"."page": 1."size": 10."data": [
        // resources]."total_pages": 20."has_previous_page": false."has_next_page": true
}
Copy the code





8. Ensure consistency

I know this sounds obvious, but it’s hard to do in practice.

A good API is predictable, meaning that when a caller uses and understands one port, the other endpoint better work the same way. This is important for the overall API and is one of the key indicators of how well designed and useful an API is.

  • Use the same case for fields, resources, and parameters
    • For naming Chinese to English variables, you can try the tool I developed, Varbook
  • Use plural or singular resource names (I don’t care about this, but I will agree on names)
    • /users/{id},/orders/{id}/user/{id},/order/{id}
  • Use the same authentication and authorization methods for all endpoints
  • Use the same HTTP header in the API
    • For example,Api-KeyUsed to pass API keys
  • Use the same HTTP status code depending on the response type
    • For example, when a resource cannot be found404
  • Use the same HTTP methods for the same types of operations
    • For example,DELETEWhen deleting a resource





9. Make exceptions to public endpoints

By default, each port should require authorization. Most ports need to invoke authenticated users, so it makes sense to set this to the default.

If you need to expose the calling endpoint, explicitly set this port to allow unauthorized requests.





Versioning API

Ensure that the API is versioned and the version is passed on every request so that users of the API are not affected by any changes to another version.

API versions can be passed using HTTP headers or query/path parameters. Even the first version of the API (1.0) should be explicitly versioned.

An incomplete example from CSDN:

  • Blog.csdn.net/community/h… .





11. Use reasonable HTTP status codes

Use traditional HTTP status codes to indicate the success or failure of a request. Don’t use too much, and use the same status code for the same results throughout the API.

Some examples:

  • 200Achieve universal success
  • 201Create for success
  • 400For incorrect requests from clients
  • 401For unauthorized requests
  • 403The lack of permissions
  • 404For missing resources
  • 429For too many requests
  • 5xxFor internal errors (which should be avoided at all costs)





12. Use self-explanatory naming

Most ports are resource-oriented and should be named in a way that is self-explanatory and does not add unnecessary information that can be inferred from elsewhere. This also applies to field names.

✅ advice

  • GET /user=> Retrieve the user
  • DELETE /user/{id}=> Log out of the user
  • POST /user/{id}/notifications=> Create notifications for specific users
  • user.nick_name

❌ don’t recommend

  • GET /getUser
  • POST /updateUser
  • POST /notification/user
  • user.NickName





13. Return the POST of the created resource

It is recommended that POST returns the created resource after it has been created using the request. This is important because the returned create resource will reflect the current state of the underlying data source and will contain updated information (such as the generated ID).

// Request: POST /register
{
    "email": "[email protected]"."name": "uiu"."web_site":"uiuing.com"
}

// Response
{
    "id": "oVUdhmcFr0"."email": "[email protected]"."name": "uiu"."web_site":"uiuing.com"
}
Copy the code





Be as specific as possible

As mentioned in Point 12, I recommend that you be as specific as possible when designing ports, naming fields, and deciding which requests and responses to accept. If only two fields (name and password) are accepted in a PATCH request, there is no danger of data corruption due to its misuse.





15. Allow resources to be extended

Allows the caller to load the relevant data with a query parameter named Expand (or similar). This is especially useful to avoid repeating and loading all the data needed for a particular operation at once.

// Request => GET /user/T9hoBuuTL4? expand=collect&expand=collect.items

// Response <= 200 OK
{
	 "id": "oVUdhmcFr0"."email": "[email protected]"."name": "uiu"."web_site":"uiuing.com"."collect": [{"id": "8161E0A4-0F5C-4A02-94A5-2880645A39E0"."items": [{"name": "How do you design a better API?"
					},
					{
						"name": "Local Storage and Session Storage"}]}, {"id": "23A7A17A-086A-4638-8EED-55E5CB580856"."items": [{"name": "[Jquery] Get started, get started, convert DOM/Jquery objects to each other}]}]}Copy the code









Don’t forget to like/bookmark/comment/follow bloggers if they help you