transformErrorResponse в RTK Query используется для нормализации ошибок, приходящих из baseQuery, и преобразования их в единый формат, удобный для дальнейшей обработки внутри endpoint’ов, reducers и UI-логики. Это механизм, который позволяет отделить транспортный уровень ошибок (HTTP, network, fetchBaseQuery) от доменной модели ошибок приложения.
При выполнении запроса через RTK Query ошибка может приходить в разных формах:
Без нормализации каждый endpoint вынужден работать с разнородными структурами, что усложняет обработку. transformErrorResponse решает эту проблему на уровне API-слайса.
Функция трансформации подключается в createApi и применяется ко всем ошибкам, возвращаемым конкретным endpoint’ом.
transformErrorResponse принимает три аргумента:
Возвращаемое значение становится error-объектом внутри RTK Query.
Базовый пример:
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react';
export const api = createApi({
reducerPath: 'api',
baseQuery: fetchBaseQuery({
baseUrl: '/api',
}),
endpoints: (builder) => ({
getUser: builder.query({
query: (id) => `user/${id}`,
transformErrorResponse: (response, meta, arg) => {
return {
status: response?.status,
message: response?.data?.message || 'Ошибка загрузки пользователя',
};
},
}),
}),
});
В результате error в useGetUserQuery будет иметь унифицированную структуру.
RTK Query передаёт в transformErrorResponse разные структуры в зависимости от baseQuery:
Типичный формат:
{
status: number,
data: any
}
или при сетевой ошибке:
{
error: string
}
meta может содержать:
arg содержит исходные параметры запроса.
Основная задача transformErrorResponse — привести разные типы ошибок к единой схеме:
{
code: string,
message: string,
details?: any
}
Пример централизованной нормализации:
transformErrorResponse: (response, meta) => {
if ('status' in response) {
return {
code: String(response.status),
message: response.data?.message || 'Server error',
details: response.data,
};
}
return {
code: 'NETWORK_ERROR',
message: response.error || 'Network failure',
};
}
Такой подход устраняет необходимость проверок формата ошибки в компонентах и селекторах.
transformErrorResponse часто используется для маппинга HTTP статусов в бизнес-ошибки:
transformErrorResponse: (response) => {
switch (response.status) {
case 400:
return {
code: 'BAD_REQUEST',
message: 'Некорректный запрос',
fields: response.data?.fields,
};
case 401:
return {
code: 'UNAUTHORIZED',
message: 'Требуется авторизация',
};
case 403:
return {
code: 'FORBIDDEN',
message: 'Доступ запрещён',
};
case 500:
return {
code: 'SERVER_ERROR',
message: 'Ошибка сервера',
};
default:
return {
code: 'UNKNOWN_ERROR',
message: 'Неизвестная ошибка',
};
}
};
Такой слой абстракции упрощает интеграцию с UI-логикой, где вместо HTTP-статусов используются доменные коды.
RTK Query позволяет типизировать transformErrorResponse для строгого контроля структуры ошибок.
type ApiError = {
code: string;
message: string;
details?: unknown;
};
type RawError = {
status: number;
data?: any;
};
transformErrorResponse: (
response: RawError,
meta: any,
arg: number
): ApiError => {
return {
code: String(response.status),
message: response.data?.message ?? 'Error',
details: response.data,
};
};
Типизация особенно важна при использовании error в RTK Query hooks, где дальнейшая обработка зависит от структуры объекта.
transformErrorResponse работает поверх baseQuery, не заменяя его поведение. Это означает, что:
Пример с кастомным baseQuery:
const baseQuery = fetchBaseQuery({
baseUrl: '/api',
});
const baseQueryWithAuth = async (args, api, extraOptions) => {
const result = await baseQuery(args, api, extraOptions);
if (result.error && result.error.status === 401) {
// логика refresh token
}
return result;
};
transformErrorResponse применяется после этого слоя, что позволяет разделять ответственность.
meta позволяет учитывать технические детали:
transformErrorResponse: (response, meta) => {
return {
code: String(response.status || 'NETWORK_ERROR'),
message: response.data?.message || 'Request failed',
url: meta?.request?.url,
method: meta?.request?.method,
time: meta?.response?.headers?.get('date'),
};
};
Это используется для логирования, мониторинга и трассировки ошибок.
Наиболее предсказуемый сценарий. response содержит status и data.
Если baseQuery возвращает собственный формат:
return {
error: {
customCode: 'E_CUSTOM',
payload: {},
}
};
transformErrorResponse должен учитывать этот формат явно:
transformErrorResponse: (response) => {
if (response.customCode) {
return {
code: response.customCode,
message: 'Custom error',
};
}
return {
code: 'UNKNOWN',
message: 'Fallback error',
};
};
Сетевые ошибки не содержат status и data:
transformErrorResponse: (response) => {
if (response?.error) {
return {
code: 'NETWORK_ERROR',
message: response.error,
};
}
return {
code: 'UNKNOWN_ERROR',
message: 'Unexpected failure',
};
};
Особенность заключается в отсутствии HTTP-уровня, что требует отдельной ветки обработки.
При росте приложения transformErrorResponse становится точкой консолидации:
Пример централизованной функции:
const normalizeError = (response, meta) => {
const status = response?.status;
if (!status) {
return {
code: 'NETWORK_ERROR',
message: response?.error || 'No connection',
};
}
const base = {
code: `HTTP_${status}`,
url: meta?.request?.url,
};
return {
...base,
message: response.data?.message || 'Request error',
};
};
transformErrorResponse: normalizeError;
После применения transformErrorResponse error в хуках RTK Query всегда имеет единый формат:
const { error } = useGetUserQuery(1);
if (error.code === 'UNAUTHORIZED') {
// обработка
}
Без transformErrorResponse структура error зависела бы от реализации backend и baseQuery.
Также важно учитывать, что чрезмерная логика внутри transformErrorResponse усложняет отладку и делает слой API менее прозрачным.
transformErrorResponse выполняет функцию адаптера между:
В зрелых архитектурах он становится частью anti-corruption layer между backend и frontend логикой, обеспечивая стабильность контрактов ошибок независимо от изменений серверной реализации.