Ізоляція API від UI
Що означає "ізолювати шар API від UI"
Ізолювати шар API - означає розділити код, який:
- спілкується з сервером (HTTP-запити, REST, GraphQL тощо), від коду, який:
- відображає інтерфейс (React-компоненти, стан, події).
Тобто UI не повинен напряму викликати:
fetch('/api/users')або знати, як саме влаштований сервер. Замість цього UI звертається до функції з окремого шару, наприклад:
// api/users.ts
export async function getUsers() {
const res = await fetch('/api/users');
return res.json();
}А в компоненті:
const { data } = useQuery({ queryKey: ['users'], queryFn: getUsers });Основна ідея
UI повинен знати лише що потрібно отримати, але не як саме це отримати.
Це - реалізація принципу Separation of Concerns (розділення відповідальності).
Чому це важливо
1. Чиста архітектура і читабельність
Коли API-логіка (запити, хедери, авторизація) змішана з UI-кодом, компоненти стають "брудними" і важкими для читання:
function UserList() {
const [users, setUsers] = useState([]);
useEffect(() => {
fetch('/api/users', { headers: { Authorization: token } })
.then(res => res.json())
.then(setUsers);
}, []);
// UI + мережеві деталі впереміш
return users.map(u => <div>{u.name}</div>);
}Після ізоляції:
// api/users.ts
export const getUsers = () =>
api.get<User[]>('/users'); // централізований fetch wrapper
// components/UserList.tsx
const { data: users } = useQuery({ queryKey: ['users'], queryFn: getUsers });Компонент займається лише відображенням, а API-код - лише запитами.
2. Переюзабельність
Якщо в кількох місцях потрібно отримати одного й того самого користувача:
useQuery({ queryKey: ['user', id], queryFn: () => getUser(id) });ти використовуєш одну й ту саму API-функцію,
а не копіюєш fetch у кожному компоненті.
3. Тестованість
Коли API ізольовано, ти можеш:
- мокати його в тестах;
- перевіряти UI без реального сервера.
Приклад:
// jest.setup.ts
jest.mock('@/api/users', () => ({
getUsers: jest.fn(() => Promise.resolve([{ id: 1, name: 'Alex' }])),
}));Тести на компонент тепер не залежать від мережі:
render(<UserList />);
expect(screen.getByText('Alex')).toBeInTheDocument();4. Централізована обробка помилок і авторизації
Коли всі запити йдуть через одну точку (apiClient),
можна:
- автоматично підставляти токен,
- перехоплювати 401 і оновлювати JWT,
- логувати помилки,
- обробляти таймаути.
// api/client.ts
export const api = axios.create({
baseURL: '/api',
});
api.interceptors.response.use(
res => res,
err => {
if (err.response?.status === 401) refreshToken();
return Promise.reject(err);
}
);Тепер не потрібно повторювати цей код у кожному компоненті.
5. Легка заміна бекенду
Якщо API змінюється (наприклад, REST → GraphQL або новий endpoint), змінюється лише шар API, а UI-код узагалі не чіпається.
// раніше
export const getUser = (id: string) => fetch(`/api/users/${id}`).then(r => r.json());
// потім
export const getUser = (id: string) =>
graphQLClient.request(GET_USER_QUERY, { id });Компоненти залишаються незмінними:
const { data } = useQuery({ queryKey: ['user', id], queryFn: () => getUser(id) });6. Інтеграція з React Query / SWR / Zustand
Сучасні менеджери даних (React Query, SWR) очікують чисту функцію:
queryFn: () => Promise<Data>Ізоляція API робить код природним:
useQuery({ queryKey: ['products'], queryFn: getProducts });→ React Query сам кешує, оновлює і синхронізує дані.
7. Відповідність архітектурним принципам
Це відповідає принципам:
- SRP (Single Responsibility Principle) - кожен модуль робить одне;
- Separation of Concerns - UI не знає про мережеві деталі;
- Dependency Inversion - React залежить від абстракції (
getUser), а не відfetch; - Clean Architecture - зовнішній шар (API) ізольований від внутрішнього (UI).
Приклад архітектури
src/
├── api/
│ ├── client.ts ← спільне налаштування fetch/axios
│ ├── users.ts ← функції API
│ └── posts.ts
├── features/
│ ├── users/
│ │ ├── hooks.ts ← useUserQuery(), useCreateUserMutation()
│ │ ├── components/
│ │ │ └── UserList.tsx
│ │ └── index.ts
├── ui/
│ ├── components/
│ └── layout/
└── app.tsxУся логіка роботи з сервером - у api/
Компоненти → хуки → API → сервер.
Підсумок
| Перевага | Що це дає |
|---|---|
| Розділення відповідальності | UI займається відображенням, API - даними |
| Централізована логіка | токени, помилки, retry в одному місці |
| Легка зміна бекенду | UI не ламається при змінах API |
| Просте тестування | можна мокати API |
| Повторне використання | однакові виклики з різних місць |
| Сумісність з React Query / SWR | чисті функції без сайд-ефектів |
Коротко:
Ізоляція шару API від UI робить застосунок чистішим, надійнішим і простішим у підтримці.
React-компоненти повинні думати лише про те, що показувати, а не як дістати дані.
Усе мережеве, авторизаційне й технічне - виноситься в шар API.
Коротка відповідь
Для співбесідиКоротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.