Standard error response format, HTTP status code mapping, error codes, client-friendly messages, and retry guidance for APIs.
Standard error response format, HTTP status code mapping, error codes, client-friendly messages, and retry guidance for APIs.
BeforeMerge offers hundreds of code review rules, guides, and detection patterns to help your team ship better code.
Consistent error handling makes APIs predictable for consumers and debuggable for operators.
interface ApiError {
error: {
code: string; // Machine-readable: "VALIDATION_ERROR"
message: string; // Human-readable: "The email field is required"
details?: Array<{ // Field-level errors for forms
field: string;
message: string;
}>;
requestId?: string; // For support/debugging
};
}| Status | Code | Use when |
|---|---|---|
| 400 | BAD_REQUEST | Malformed JSON, missing required fields |
| 401 | UNAUTHORIZED | No auth token or expired token |
| 403 | FORBIDDEN | Valid auth but insufficient permissions |
| 404 | NOT_FOUND | Resource does not exist |
| 409 | CONFLICT | Duplicate entry, version conflict |
| 422 | UNPROCESSABLE_ENTITY | Valid JSON but business rule violation |
| 429 | RATE_LIMITED | Too many requests |
| Status | Code | Use when |
|---|---|---|
| 500 | INTERNAL_ERROR | Unexpected server failure |
| 502 | BAD_GATEWAY | Upstream service returned invalid response |
| 503 | SERVICE_UNAVAILABLE | Planned maintenance or overload |
| 504 | GATEWAY_TIMEOUT | Upstream service timed out |
class AppError extends Error {
constructor(
public statusCode: number,
public code: string,
message: string,
public details?: Array<{ field: string; message: string }>
) {
super(message);
}
}
// Usage
throw new AppError(404, "NOT_FOUND", "User not found");
throw new AppError(422, "VALIDATION_ERROR", "Invalid input", [
{ field: "email", message: "must be a valid email address" },
]);app.use((err: Error, req: Request, res: Response, next: NextFunction) => {
const requestId = req.headers["x-request-id"] || crypto.randomUUID();
if (err instanceof AppError) {
return res.status(err.statusCode).json({
error: {
code: err.code,
message: err.message,
details: err.details,
requestId,
},
});
}
// Unexpected errors — log full details, return generic message
console.error({ requestId, error: err.message, stack: err.stack });
return res.status(500).json({
error: {
code: "INTERNAL_ERROR",
message: "An unexpected error occurred",
requestId,
},
});
});Include retry information in responses:
{
"error": {
"code": "RATE_LIMITED",
"message": "Too many requests",
"retryAfter": 30
}
}Retry rules:
retryAfter)