I'm backend engineer and biggest part of my career based on creating different APIs for services. Tips for this article were collected based on the most frequently faced issues on designing stage of team projects or using external APIs.
Chances are, you've come across terrible API providers. Working with them, as a rule, is associated with increased emotionality and misunderstanding. Some of these problems can be avoided by designing the application interface using the tips below.
1. Do not use verbs in url*
*
- if you are going to implement one of CRUD-action
The CRUD request methods are responsible for the action with the resource: POST - create, GET - read, PUT / PATH - update , DELETE - delete (you know :)).
Badly:
POST /users/{userId}/delete - delete user
POST /bookings/{bookingId}/update - update booking
Good:
DELETE /users/{userId}
PUT /bookings/{bookingId}
2. Use verbs in url
You can use verbs in url to describe something diff than CRUD-action.
Badly:
POST /users/{userId}/books/{bookId}/create - link book with user
Good:
POST /users/{userId}/books/{bookId}/attach
POST /users/{userId}/notifications/send - send notify to user
3. Define new entities
Above there is an example of adding a book to a user, perhaps the logic of your application implies a list of favorites, then the route may be like this:
POST /favorite/{userId}/{bookId}
4. Try to use one resource identifier
This means if you have one-to-many records, for example: booking -> travellers, it will be enough for you to use only traveler identifier in the request.
Badly:
# getting travellers data
GET /bookings/{bookingId}/travellers/{travellerId}
Good:
GET /bookings/travellers/{travellerId}
Also note that /bookings/travellers
is better than just /travellers
. It's good to stick to the data hierarchy in your API.
5. All resources in plural
Badly:
GET /user/{userId} - getting users data
POST /ticket/{ticketId}/book - ticket booking process
Good:
GET /users/{userId}
POST /tickets/{ticketId}/book
6. Make the most of HTTP statuses
The easiest way to handle errors is to respond with an appropriate status code. In most cases, this status alone can provide comprehensive information about the result of request processing. Some of the most common response codes:
400 Bad Request
- The client sent an invalid request, for example, a required request parameter is missing.401 Unauthorized
- The client was unable to pass mandatory server authentication to process the request.403 Forbidden
- The client is authenticated but does not have permission to access the requested resource.404 Not Found
- The requested resource does not exist.409 Conflict
- This response is sent when the request conflicts with the current state of the server. Example: trying to make an booking twice.500 Internal Server Error
- A general error has occurred on the server.503 Service Unavailable
- The requested service is not available. Example: slow work of backend code and, consequently, freezing of the general workflow of service.
7. Resource get modifiers
The routing logic may not be related to the project architecture or database structure. For example, the database contains quizzes and passed quizzes - two separate tables and entities (quizzes
and passed_quizzes
). But for api, it can be just quizzes, and passed quizzes are a modifier.
Route example: /quizzes
and /quizzes/passed
. Here quizzes
- resource, passed
- modifier.
Badly:
GET /passed-quizzes - getting passed quizzes
GET /booked-tickets - getting booked tickets
POST /gold-users - creating gold user
Good:
GET /tickets/booked
POST /users/gold
8. Choose one response structure
When on two different API requests can be received completely different response - it's sadly.
Try to form one clear structure that you will always adhere to. It will also be cool to include service fields that carry additional information.
Badly:
GET /book/{bookId}
{
"name": "Harry Potter and the Philosopher's Stone",
"genre": "fantasy",
"status": 0, # статус вашего приложения
"error": false,
...
}
Good:
GET /book/{bookId}
{
"status": 0,
"message": "ok",
"data": {...}
}
In this example, 3 fields are universal and can be used for any responses from your API. status
- request-handling status of application. messages
- text by which API-client can figure that happening. Both fields inform additional information about request processing, but they shouldn't include application data.
For example, in application at one time, a user can take only one quiz. Then a request to start new quiz can reply the 409 status code. In the status and message fields there will be additional information why the error has occurred.
9. All parameters and json in camelCase
9.1 Request params
Badly:
GET /users/{user-id}
GET /users/{user_id}
GET /users/{userid}
Good:
GET /users/{userId}
POST /ticket/{ticketId}/gold
9.2 In body of request or response
Badly:
{
"ID": "fb6ad842-bd8d-47dd-b7e1-68891d8abeec",
"Name": "soccer3000",
"provider_id": 1455,
"Created_At": "25.05.2020"
}
Good:
{
"id": "fb6ad842-bd8d-47dd-b7e1-68891d8abeec",
"name": "soccer3000",
"providerId": 1455,
"createdAt": "25.05.2020"
}
10. Use Content-Type
Don't make special routes for types, use Content-Type
header instead.
Badly:
GET /tickets.json
GET /tickets.xml
Good:
GET /tickets
// in headers
Сontent-Type: application/json
// or
Сontent-Type: application/xml
The End
The tips listed above are not the whole list of ways to make the API better. For further study, I recommend that you discover the REST API specifications and the list of http status codes (you will be surprised how many of them and what situations they cover).
Let's share in comments your recommendations for building awesome APIs.
Happy coding!
Top comments (0)