The HyperText Transfer Protocol (HTTP) 400 Bad Request
response status code indicates that the server cannot or will not process the request due to something that is perceived to be a client error (for example, malformed request syntax, invalid request message framing, or deceptive request routing).
[source] MDN
Problem Statement
Our backend services know when to raise HTTP 400 status code. Any malformed request which doesn’t follow the API contract can be detected using validations on the backend against the schema. If the validation fails, 400 is raised. However, there is another problem statement.
How do you pass the validation errors to the client? It can be single or multiple.
We need to think about how the client will parse that information to warn the users.
Let’s take a very simple example. Let’s take a service that handles the creation of users.
| Attribute | Description |
|----------- |---------------------|
| username | The user's username |
| email | The user's email |
| password | The user's password |
Each of these fields can have its own validations.
The username should not contain special characters.
Email should be in a proper format
Password needs to be secure
If any validation fails, the standard is to return a 400 status code. But if we just return the status code, the client will never know “The specific User attributes for which the validation failed.“
How do you pass the validation errors to the client so that appropriate error messages can be shown to the user?
Solution Statement
Instead of simply raising a generic 400 Bad Request error, you can provide more detailed and specific validation errors to the client. This helps in providing clearer feedback to the user about what went wrong and which fields need to be corrected. Here are a few approaches you can consider:
Error Response Object: Define a structured error response object that contains information about the validation errors. For example, you can include an array of error messages, where each message specifies the field or parameter that failed validation and a corresponding error description. This allows the client to parse and display the errors appropriately.
Error Codes or Error Keys: Instead of returning error messages directly, you can use error codes or keys to identify different types of validation errors. The client can then map these codes to localized error messages based on the user's language preference. This approach provides flexibility for localization and centralizes error message management.
Detailed Error Descriptions: Include additional details in the error response to help the client understand the nature of the validation failure. For example, you can include information about the expected format, allowed values, or specific rules that were violated. This empowers the client to present more informative error messages to the user.
Validate and Accumulate Errors: Rather than stopping at the first encountered validation error, continue validating the entire request or input payload to collect all validation failures. This allows you to gather multiple errors and return them all to the client. By following this approach, the client can address multiple issues at once, rather than submitting the request multiple times to fix one error at a time.
HTTP Status Codes: Alongside the detailed validation errors, ensure that you use appropriate HTTP status codes to indicate the overall outcome of the request. For example, you can still use 400 Bad Request for general validation errors but include the detailed error response as explained above. Additionally, you can use more specific HTTP status codes like 422 Unprocessable Entity for cases where the request is syntactically correct but contains semantic errors.
By implementing these strategies, you can provide more informative and granular validation errors to the client, enabling better user experience and faster resolution of issues.