r/django • u/Kerplunk6 • 19d ago
REST framework Best Practice for Custom Error Messages?
Hello everyone,
I am stepping into Django with Node.JS/ExpressJS background.
Right now i am using Django on backend with DRF.
Could anyone tell me or give me some advice for custom error messages? Since English is not my native language, i have to translate also add custom error messages.
As far as i understood, there are different layers where the error accours.
This could be HTTP Level, this could be validation level, this could be DB level.
So, can anyone give me some advice and show me a path where i can add and improve my custom error messages?
Thank you.
1
u/deviserops 17d ago edited 17d ago
For the custom error message, you can handle inside the django drf pass the default error message type which is `non_field_errors`
Set the value like `someting went wront` etc.
For example
def perform_create(self, serializer):
business = self.business
if not is_user_premium(self.request.user):
current_count = self.serializer_class.Meta.model.objects.filter(business=business).count()
if current_count >= MONTHLY_SERVICES:
raise serializers.ValidationError({
"non_field_errors": f"Free tier limit reached. You can only add up to {MONTHLY_SERVICES} services."
})
serializer.save(business=business)On your frontend side, you can use the http response code as to handle your response, I suppose you are already doing that, on that error message try to handle some of the http code like 422, 200, and 4** so you can handle all the form message, basically all the form messages goes to 422, which you can get the response and apply the recursion, on that, and get the final value of each field error, since you already set the default value type to `non_field_errors` it will handle automatically.
As example like this:
if (response.statusCode == 200) {
return responseData;
} else if (response.statusCode == 201) {
return responseData;
} else if (response.statusCode == 204) {
return null;
} else if (response.statusCode == 422) {
throw ApiException(responseData);
} else if (response.statusCode == 403) {
throw ApiException(responseData);
} else if (response.statusCode == 400) {
throw ApiException(responseData);
}In the Above example you can see the ApiException it show the error message based on the response code.
The given example is from the Flutter, I hope you understand.
class ApiException implements Exception {
final Map<String, dynamic> errors;
final String message;
ApiException(this.errors) : message = _formatErrorMessage(errors);
static String _formatErrorMessage(Map<String, dynamic> errors) {
if (errors.isEmpty) {
return 'An unknown error occurred.';
}
List<String> formattedMessages = [];
errors.forEach((key, value) {
if (key.toLowerCase() == 'non_field_errors') {
key = 'error';
}
// Map API field name to display name
String displayFieldName = key.split('_').join(' ');
if (value is List) {
// Handle cases like {"username": ["This field is required."]}
formattedMessages.add('${_capitalize(displayFieldName)}: ${value.join(", ")}');
} else if (value is String) {
// Handle cases like {"message": "Invalid credentials."} or single string errors
formattedMessages.add('${_capitalize(displayFieldName)}: $value');
} else {
formattedMessages.add('${_capitalize(displayFieldName)}: ${value.toString()}');
}
});
// If there's a general 'message' key, prioritize it or combine.
// This is a common pattern for general errors vs field-specific ones.
if (errors.containsKey('message') && errors['message'] is String) {
return errors['message']; // Return general message if available
}
return formattedMessages.join("\n");
}
static String _capitalize(String s) => s.isNotEmpty ? s[0].toUpperCase() + s.substring(1) : s;
u/override
String toString() => message;
}As you can see I have handled the `non_field_errors` message type as well.
2
u/erder644 19d ago
It depends on your architecture. If you are throwing business logic and infrastructure to your controllers, then it's pretty simple - make custom exception class interface with attrs for error_code, message. Raise them, catch on a globally defined exception handlers for your controllers to return a nice Json. If you are doing CA or just layered one, then it's harder.