Common REST API mistakes

REST APIs are now used in the majority of web applications. They allow different services to communicate easily using the HTTP protocol. However, designing a good REST API is not always easy, and some mistakes are frequently made by developers. I have made quite a few myself...
A poorly designed API can quickly become difficult to maintain, complicated for clients to use, and a source of many bugs. Here are some of the most common mistakes and how to avoid them.
Misusing HTTP status codes
HTTP provides many status codes to clearly indicate the result of a request. However, many APIs still use 200 OK even when the request has failed.
For example, returning:
{
"error": "User not found"
}
with an HTTP 200 status code forces the client to analyze the response content to understand that an error occurred, which is really not ideal.
It is better to use the appropriate HTTP status codes:
200 OK: the request was successful;201 Created: a resource was created;204 No Content: the request was successful but no data is returned;400 Bad Request: the request is invalid;401 Unauthorized: authentication is required or invalid;403 Forbidden: the user is authenticated but does not have the required permissions;404 Not Found: the requested resource does not exist;500 Internal Server Error: an unexpected error occurred on the server side.
Confusing 400, 401, 403 and 404
These four status codes are often misused.
400 Bad Request
This code indicates that the request sent by the client is invalid.
Example:
{
"email": "invalid-email"
}
The server understands the request, but the provided data does not comply with the expected rules.
401 Unauthorized
Despite its name, this code generally means "not authenticated".
Examples:
- missing JWT token;
- expired token;
- invalid API key.
The client must provide valid authentication.
403 Forbidden
The user is correctly identified, but they do not have permission to perform the action.
Example:
A logged-in user tries to delete an account that does not belong to them.
404 Not Found
The requested resource does not exist.
Example:
GET /users/999999
if no user exists with this identifier.
Poor error handling
An API should always return structured and consistent errors.
Bad example:
{
"message": "Something went wrong"
}The client does not really know what happened.
A more useful format:
{
"error": {
"code": "INVALID_EMAIL",
"message": "The email address is invalid"
}
}This allows client applications to handle errors automatically.
You should also avoid returning sensitive information in error messages, such as SQL traces or internal server details, for obvious security reasons (This point is really critical nowadays).
Choosing the right HTTP methods
A common mistake in REST APIs is using HTTP methods without respecting their original purpose.
The main methods are:
GET: retrieve one or more resources. This method should not modify data.POST: create a new resource or trigger an action on the server side.PUT: completely replace an existing resource. It is generally used when sending the full representation of a resource.PATCH: partially update a resource. For example, updating only a user's name without sending all other fields.DELETE: delete a resource.
For example:
GET /users/123retrieves the user with the identifier 123:
PATCH /users/123can only update their email address:
{
"email": "new@email.com"
}Whereas:
PUT /users/123generally expects a complete representation of the user.
Recently, we have had a new addition with the arrival of the HTTP QUERY method, defined in RFC 9110 as a method allowing read requests with a request body. It addresses the needs of APIs where search parameters become too complex to be sent only through a URL using query parameters.
Unlike GET, QUERY allows structured search criteria to be sent in the request body while keeping a read-only semantic. This approach can be useful for advanced searches or complex filters, but it is still rarely used compared to traditional HTTP methods.
Poor pagination management
Returning all the data from a table can quickly become problematic, both for the client and the server.
A request like:
GET /usersmay work with a few hundred users, but become impossible to handle with millions of records.
An API should provide a pagination system:
GET /users?page=2&limit=50The response can include additional information:
{
"data": [],
"pagination": {
"page": 2,
"limit": 50,
"total": 1500
}
}
For very large amounts of data, cursor pagination can be more efficient than traditional page-based pagination.
Neglecting versioning
An API evolves over time. Changing an existing response without notifying clients can break applications in production.
A common solution is to version APIs:
GET /api/v1/usersThen later:
GET /api/v2/usersThis allows multiple versions to be maintained during a transition period. It is often difficult to make a big bang migration, and some clients are sometimes not very motivated to use new versions.
Forgetting about idempotence
An operation is idempotent when it produces the same result even if it is executed multiple times.
For example:
PUT /users/123to update a user is generally idempotent.
Sending this twice:
{
"name": "John"
}will produce the same final result.
However, creating a resource with:
POST /ordersmay create two orders if the request is sent twice.
To avoid this problem, some APIs use idempotency keys:
Idempotency-Key: abc123
This is especially useful for payments.
Poor authentication management
Authentication is a critical part of an API.
Some common mistakes:
- sending passwords directly in requests;
- storing tokens without expiration;
- using HTTP instead of HTTPS;
- not checking permissions after authentication;
- exposing sensitive information in responses.
Common approaches include:
- API keys for simple services;
- OAuth 2.0 for applications requiring delegated access;
- JWT for transmitting signed authentication information.
However, a JWT is not a magic solution. Its lifetime, revocation, and client-side storage must be properly managed.
Conclusion
Designing a good REST API is not just about creating a few HTTP routes. You also need to think about response codes, error handling, security, and the future evolution of the API.
By following good practices such as correctly using HTTP status codes, implementing consistent error handling, versioning APIs, and using appropriate authentication, you will build APIs that are easier to use and maintain.
A well-designed API allows client developers to save time and significantly reduces problems when the project evolves.



Laisser un commentaire