Конфигурация базового URL

Базовый 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 заканчивается слешем
  • endpoint не начинается со слеша

Конфигурация для локального сервера

Во время разработки часто используется локальный 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

Использование API-версий

Во многих REST API применяется версионирование.

Пример:

https://api.example.com/v1/

Конфигурация:

baseUrl: 'https://api.example.com/v1/'

Endpoints:

query: () => 'users'

Итог:

https://api.example.com/v1/users

Переключение между окружениями

Обычно проект имеет несколько окружений:

  • development
  • staging
  • production

Жёстко прописывать URL в коде неудобно.


Использование переменных окружения

Vite

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',
        }),
    }),
});

Create React App

REACT_APP_API_URL=https://api.example.com/

Использование:

baseUrl: process.env.REACT_APP_API_URL

Централизация конфигурации

Часто базовый адрес выносится в отдельный конфигурационный файл.

config/api.js

export const API_URL = 'https://api.example.com/';

Использование:

import { API_URL } from './config/api';

baseQuery: fetchBaseQuery({
    baseUrl: API_URL,
})

Создание нескольких API

Иногда приложение работает сразу с несколькими backend-сервисами.

Пример:

  • основной API
  • файловый сервис
  • аналитический сервис

Отдельный API для каждого сервиса

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: () => ({}),
});

Абсолютный URL внутри endpoint

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

Иногда URL зависит от состояния приложения.

Например:

  • регион пользователя
  • выбранный сервер
  • multi-tenant архитектура
  • язык
  • поддомен клиента

Кастомный 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',
        }),
    }),
});

Прокси и базовый URL

Во frontend-разработке часто используется proxy.

Например, Vite:

server: {
    proxy: {
        '/api': {
            target: 'http://localhost:8000',
            changeOrigin: true,
        },
    },
}

Тогда:

baseUrl: '/api/'

Запрос:

/api/users

Будет автоматически перенаправлен на:

http://localhost:8000/users

Преимущества proxy

Отсутствие CORS-проблем

Frontend и backend визуально находятся на одном origin.


Удобство локальной разработки

Можно менять backend без изменения frontend-кода.


Единый API-префикс

Все запросы централизуются через:

/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;
    },
})

Полная конфигурация API

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'

Начальный слеш в endpoint

query: () => '/users'

Дублирование /api

baseUrl: 'https://api.example.com/api/'
query: () => 'api/users'

Результат:

https://api.example.com/api/api/users

Использование неправильного окружения

Частая проблема:

VITE_API_URL=http://localhost:3000/

в production-сборке.


Жёстко прописанные URL

Плохо:

query: () =>
    'https://api.example.com/users'

Такой подход разрушает централизованную конфигурацию.


Архитектурные рекомендации

Централизованный API-слой

Весь сетевой доступ должен проходить через RTK Query API slices.


Один источник конфигурации

Базовый URL хранится:

  • либо в env
  • либо в config
  • либо в runtime-конфигурации

Разделение сервисов

Для разных backend-доменов лучше создавать отдельные API slices.


Отказ от хардкода

Все адреса должны быть параметризованы.


Пример production-структуры

api/baseQuery.js

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;
    },
});

api/index.js

import { createApi } from '@reduxjs/toolkit/query/react';

import { baseQuery } from './baseQuery';

export const api = createApi({
    reducerPath: 'api',

    baseQuery,

    endpoints: () => ({}),
});

api/users.js

import { api } from './index';

export const usersApi = api.injectEndpoints({
    endpoints: (builder) => ({
        getUsers: builder.query({
            query: () => 'users',
        }),
    }),
});

export const {
    useGetUsersQuery,
} = usersApi;

Поведение RTK Query при формировании URL

RTK Query не использует сложную нормализацию адресов.

Объединение выполняется достаточно прямолинейно:

baseUrl + url

Поэтому корректность слешей полностью зависит от разработчика.


Поддержка HTTPS

В production практически всегда используется HTTPS:

baseUrl: 'https://api.example.com/'

Использование HTTP может привести к:

  • Mixed Content
  • блокировке браузером
  • проблемам безопасности
  • невозможности работы Service Worker
  • ограничениям современных API браузера

Работа с CDN и gateway

В enterprise-проектах API может находиться за:

  • API Gateway
  • Nginx
  • Kong
  • Traefik
  • Cloudflare
  • CDN

В этом случае baseUrl обычно указывает не на сам backend, а на gateway-уровень:

baseUrl: 'https://gateway.example.com/api/'

Гибридная конфигурация

Иногда часть API локальная, а часть внешняя.

Пример:

baseUrl: '/api/'

Но отдельный endpoint:

query: () => ({
    url: 'https://external-service.com/data',
})

Такой подход позволяет совмещать proxy и внешние сервисы внутри одного API slice.