Localize Axios HTTP Client Error Responses
Handling API errors gracefully requires translating raw HTTP status codes or backend error codes into user-friendly, localized messages based on the user's active language. This article demonstrates how to implement a centralized error-mapping architecture using Axios interceptors, translation dictionaries, and fallback strategies to display localized error messages throughout your application.
The Localization Workflow
A robust error-localization workflow consists of three primary components:
- Standardized Error Identification: Relying on HTTP
status codes (e.g.,
404,500) or specific backend application error codes (e.g.,AUTH_INVALID_CREDENTIALS). - Translation Dictionaries: Key-value pairs matching error codes to localized strings.
- Axios Response Interceptors: Middleware that intercepts failed requests, maps the error to the correct translated string, and normalizes the error object before it reaches UI components.
Step 1: Define Localization Dictionaries
Create translation files containing keys for common HTTP status codes and specific application error codes.
locales/en.json
{
"errors": {
"network_error": "Network unavailable. Please check your internet connection.",
"timeout": "The server took too long to respond. Please try again.",
"unknown": "An unexpected error occurred. Please try again later.",
"http": {
"400": "Invalid request. Please check your input.",
"401": "Your session has expired. Please log in again.",
"403": "You do not have permission to perform this action.",
"404": "The requested resource could not be found.",
"422": "Validation failed.",
"500": "Internal server error. Please try again later."
},
"app": {
"USER_NOT_FOUND": "No account found with the provided email address.",
"INSUFFICIENT_FUNDS": "Your account balance is insufficient for this transaction."
}
}
}locales/es.json
{
"errors": {
"network_error": "Red no disponible. Por favor, compruebe su conexión a internet.",
"timeout": "El servidor tardó demasiado en responder. Inténtelo de nuevo.",
"unknown": "Ocurrió un error inesperado. Por favor, inténtelo más tarde.",
"http": {
"400": "Solicitud no válida. Por favor, compruebe sus datos.",
"401": "Su sesión ha caducado. Por favor, inicie sesión nuevamente.",
"403": "No tiene permiso para realizar esta acción.",
"404": "No se pudo encontrar el recurso solicitado.",
"422": "Error de validación.",
"500": "Error interno del servidor. Por favor, inténtelo más tarde."
},
"app": {
"USER_NOT_FOUND": "No se encontró ninguna cuenta con el correo proporcionado.",
"INSUFFICIENT_FUNDS": "El saldo de su cuenta es insuficiente para esta transacción."
}
}
}Step 2: Implement the Error Mapping Helper
Create a helper function that resolves an Axios error to a localized
message key. This example uses a generic translation function
t(key) compatible with libraries like
i18next.
// errorMapper.js
import i18n from './i18n'; // Your i18n instance
export function getLocalizedErrorMessage(error) {
// 1. Handle network and timeout errors
if (error.code === 'ECONNABORTED') {
return i18n.t('errors.timeout');
}
if (!error.response) {
return i18n.t('errors.network_error');
}
const { status, data } = error.response;
// 2. Check for application-specific error code from backend
if (data && data.errorCode) {
const appKey = `errors.app.${data.errorCode}`;
if (i18n.exists(appKey)) {
return i18n.t(appKey, data.params || {});
}
}
// 3. Check for standard HTTP status code
const httpKey = `errors.http.${status}`;
if (i18n.exists(httpKey)) {
return i18n.t(httpKey);
}
// 4. Default fallback
return i18n.t('errors.unknown');
}Step 3: Attach the Axios Response Interceptor
Configure an Axios instance with a response interceptor to automatically attach the localized message to rejected promises.
// apiClient.js
import axios from 'axios';
import { getLocalizedErrorMessage } from './errorMapper';
const apiClient = axios.create({
baseURL: 'https://api.example.com',
timeout: 10000,
});
apiClient.interceptors.response.use(
(response) => response,
(error) => {
// Map the error to a localized user-facing message
const localizedMessage = getLocalizedErrorMessage(error);
// Attach the message directly to the error object
error.localizedMessage = localizedMessage;
return Promise.reject(error);
}
);
export default apiClient;Step 4: Consume Localized Errors in the UI
UI components can now read the error.localizedMessage
property directly without needing to duplicate error-parsing logic.
import apiClient from './apiClient';
async function handleFormSubmit(payload) {
try {
const response = await apiClient.post('/users/update', payload);
return response.data;
} catch (error) {
// Display the localized string to the user
showToastNotification(error.localizedMessage);
}
}Best Practices
- Dynamic Interpolation: When backend errors contain
dynamic values (e.g.,
{ minLength: 8 }), pass the parameters directly into the translation function to render placeholders like"Password must be at least {{minLength}} characters." - Avoid Exposing Raw Server Errors: Never fall back
to
error.messageorresponse.data.messagein production UI components, as this can expose sensitive stack traces and bypass localization. - Support Language Changes: If the user switches languages dynamically, ensure the localization library updates the active instance so subsequent intercepted requests resolve to the new language immediately.