Базовый URL (baseUrl) в RTK Query определяет общий адрес
API, к которому будут выполняться запросы. Конфигурация выполняется
внутри fetchBaseQuery и служит фундаментом всей сетевой
архитектуры приложения.
Вместо постоянного дублирования полного адреса:
https://api.example.com/users
https://api.example.com/posts
https://api.example.com/comments
достаточно определить общий корень:
baseUrl: 'https://api.example.com/'
После этого endpoints указывают только относительные пути:
/users
/posts
/comments
Это уменьшает дублирование, упрощает поддержку и делает API-конфигурацию централизованной.
baseUrlМинимальная конфигурация выглядит следующим образом:
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react';
export const api = createApi({
reducerPath: 'api',
baseQuery: fetchBaseQuery({
baseUrl: 'https://api.example.com/',
}),
endpoints: (builder) => ({
getUsers: builder.query({
query: () => 'users',
}),
}),
});
export const {
useGetUsersQuery,
} = api;
RTK Query автоматически объединяет:
baseUrl + endpoint
Итоговый URL:
https://api.example.com/users
Одна из самых распространённых проблем — неправильное объединение URL из-за слешей.
baseUrl: 'https://api.example.com/'
query: () => 'users'
Результат:
https://api.example.com/users
baseUrl: 'https://api.example.com'
query: () => 'users'
Иногда такой код работает корректно, но в сложных случаях появляются ошибки формирования URL.
baseUrl: 'https://api.example.com/'
query: () => '/users'
Результат:
https://api.example.com//users
Многие серверы обрабатывают это нормально, но часть backend-инфраструктуры, CDN, reverse proxy или gateway могут воспринимать такие URL некорректно.
Наиболее стабильный и распространённый подход:
baseUrl: 'https://api.example.com/'
И далее:
query: () => 'users'
query: () => 'posts'
query: () => 'comments'
То есть:
baseUrl заканчивается слешемВо время разработки часто используется локальный backend.
baseUrl: 'http://localhost:3000/api/'
Либо:
baseUrl: 'http://127.0.0.1:8000/'
Пример:
export const api = createApi({
reducerPath: 'api',
baseQuery: fetchBaseQuery({
baseUrl: 'http://localhost:3000/api/',
}),
endpoints: (builder) => ({
getPosts: builder.query({
query: () => 'posts',
}),
}),
});
Запрос:
http://localhost:3000/api/posts
Во многих REST API применяется версионирование.
Пример:
https://api.example.com/v1/
Конфигурация:
baseUrl: 'https://api.example.com/v1/'
Endpoints:
query: () => 'users'
Итог:
https://api.example.com/v1/users
Обычно проект имеет несколько окружений:
Жёстко прописывать URL в коде неудобно.
VITE_API_URL=https://api.example.com/
Использование:
baseUrl: import.meta.env.VITE_API_URL
Полный пример:
export const api = createApi({
reducerPath: 'api',
baseQuery: fetchBaseQuery({
baseUrl: import.meta.env.VITE_API_URL,
}),
endpoints: (builder) => ({
getUsers: builder.query({
query: () => 'users',
}),
}),
});
REACT_APP_API_URL=https://api.example.com/
Использование:
baseUrl: process.env.REACT_APP_API_URL
Часто базовый адрес выносится в отдельный конфигурационный файл.
export const API_URL = 'https://api.example.com/';
Использование:
import { API_URL } from './config/api';
baseQuery: fetchBaseQuery({
baseUrl: API_URL,
})
Иногда приложение работает сразу с несколькими backend-сервисами.
Пример:
export const authApi = createApi({
reducerPath: 'authApi',
baseQuery: fetchBaseQuery({
baseUrl: 'https://auth.example.com/',
}),
endpoints: () => ({}),
});
export const filesApi = createApi({
reducerPath: 'filesApi',
baseQuery: fetchBaseQuery({
baseUrl: 'https://files.example.com/',
}),
endpoints: () => ({}),
});
RTK Query позволяет переопределять baseUrl.
Пример:
getExternalData: builder.query({
query: () => ({
url: 'https://external-api.com/data',
}),
})
В этом случае абсолютный URL имеет приоритет над
baseUrl.
Внутри query допускаются различные варианты:
query: () => 'users'
query: () => ({
url: 'users',
})
query: (id) => `users/${id}`
query: ({ id }) => ({
url: `users/${id}`,
})
Все они объединяются с baseUrl.
Иногда URL зависит от состояния приложения.
Например:
baseQueryДля динамического URL создаётся собственный wrapper.
import {
createApi,
fetchBaseQuery,
} from '@reduxjs/toolkit/query/react';
const dynamicBaseQuery = async (
args,
api,
extraOptions,
) => {
const state = api.getState();
const region = state.settings.region;
const baseUrl =
region === 'eu'
? 'https://eu-api.example.com/'
: 'https://us-api.example.com/';
const rawBaseQuery = fetchBaseQuery({
baseUrl,
});
return rawBaseQuery(
args,
api,
extraOptions,
);
};
export const apiService = createApi({
reducerPath: 'api',
baseQuery: dynamicBaseQuery,
endpoints: (builder) => ({
getUsers: builder.query({
query: () => 'users',
}),
}),
});
Во frontend-разработке часто используется proxy.
Например, Vite:
server: {
proxy: {
'/api': {
target: 'http://localhost:8000',
changeOrigin: true,
},
},
}
Тогда:
baseUrl: '/api/'
Запрос:
/api/users
Будет автоматически перенаправлен на:
http://localhost:8000/users
Frontend и backend визуально находятся на одном origin.
Можно менять backend без изменения frontend-кода.
Все запросы централизуются через:
/api/
Базовый URL часто настраивается вместе с заголовками.
baseQuery: fetchBaseQuery({
baseUrl: 'https://api.example.com/',
prepareHeaders: (headers, { getState }) => {
const token = getState().auth.token;
if (token) {
headers.set(
'Authorization',
`Bearer ${token}`
);
}
return headers;
},
})
import {
createApi,
fetchBaseQuery,
} from '@reduxjs/toolkit/query/react';
export const api = createApi({
reducerPath: 'api',
baseQuery: fetchBaseQuery({
baseUrl: 'https://api.example.com/v1/',
prepareHeaders: (
headers,
{ getState }
) => {
const token =
getState().auth.token;
if (token) {
headers.set(
'Authorization',
`Bearer ${token}`
);
}
headers.set(
'Content-Type',
'application/json'
);
return headers;
},
}),
endpoints: (builder) => ({
getUsers: builder.query({
query: () => 'users',
}),
getUser: builder.query({
query: (id) => `users/${id}`,
}),
createUser: builder.mutation({
query: (body) => ({
url: 'users',
method: 'POST',
body,
}),
}),
}),
});
baseUrl: 'https://api.example.com'
query: () => '/users'
/apibaseUrl: 'https://api.example.com/api/'
query: () => 'api/users'
Результат:
https://api.example.com/api/api/users
Частая проблема:
VITE_API_URL=http://localhost:3000/
в production-сборке.
Плохо:
query: () =>
'https://api.example.com/users'
Такой подход разрушает централизованную конфигурацию.
Весь сетевой доступ должен проходить через RTK Query API slices.
Базовый URL хранится:
Для разных backend-доменов лучше создавать отдельные API slices.
Все адреса должны быть параметризованы.
import { fetchBaseQuery } from '@reduxjs/toolkit/query/react';
export const baseQuery = fetchBaseQuery({
baseUrl: import.meta.env.VITE_API_URL,
prepareHeaders: (headers, api) => {
const token =
api.getState().auth.token;
if (token) {
headers.set(
'Authorization',
`Bearer ${token}`
);
}
return headers;
},
});
import { createApi } from '@reduxjs/toolkit/query/react';
import { baseQuery } from './baseQuery';
export const api = createApi({
reducerPath: 'api',
baseQuery,
endpoints: () => ({}),
});
import { api } from './index';
export const usersApi = api.injectEndpoints({
endpoints: (builder) => ({
getUsers: builder.query({
query: () => 'users',
}),
}),
});
export const {
useGetUsersQuery,
} = usersApi;
RTK Query не использует сложную нормализацию адресов.
Объединение выполняется достаточно прямолинейно:
baseUrl + url
Поэтому корректность слешей полностью зависит от разработчика.
В production практически всегда используется HTTPS:
baseUrl: 'https://api.example.com/'
Использование HTTP может привести к:
В enterprise-проектах API может находиться за:
В этом случае baseUrl обычно указывает не на сам
backend, а на gateway-уровень:
baseUrl: 'https://gateway.example.com/api/'
Иногда часть API локальная, а часть внешняя.
Пример:
baseUrl: '/api/'
Но отдельный endpoint:
query: () => ({
url: 'https://external-service.com/data',
})
Такой подход позволяет совмещать proxy и внешние сервисы внутри одного API slice.