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:
POST
Used to create resourcesPOST /user
GET
For reading resources (individual resources and collections)GET /user
GET /user/{id}
PATCH
Used to apply partial updates to a resourcePATCH /user/{id}
PUT
Used to apply full updates to a resource (replacing the current resource)PUT /user/{id}
DELETE
Used to delete a resourceDELETE /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,PATCH
Instead 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 using
PUT
Updating 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-Key
Used to pass API keys
- For example,
- Use the same HTTP status code depending on the response type
- For example, when a resource cannot be found
404
- For example, when a resource cannot be found
- Use the same HTTP methods for the same types of operations
- For example,
DELETE
When deleting a resource
- For example,
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:
200
Achieve universal success201
Create for success400
For incorrect requests from clients401
For unauthorized requests403
The lack of permissions404
For missing resources429
For too many requests5xx
For 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 userDELETE /user/{id}
=> Log out of the userPOST /user/{id}/notifications
=> Create notifications for specific usersuser.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