Giriş ve Problem Tanımı
Büyük ölçekli React ve Next.js projelerinde API isteklerini yönetmek kritik bir mimari karardır. Her bileşende ayrı ayrı `axios.get()` veya `fetch()` çağrısı yapmak, header yönetimini tekrarlamak ve hata yakalamayı bileşen seviyesinde çözmeye çalışmak kod karmaşasına (spaghetti code) ve bakım maliyetlerinin artmasına yol açar.
Bu rehberde, merkezi bir **Axios Interceptor** ve **Custom Service** katmanı kurarak API iletişimini nasıl standartlaştıracağımızı inceliyoruz.
1. Merkezi HTTP İstemcisi ve Interceptor Mimarisi
İlk adımda tüm HTTP isteklerinin ve yanıtlarının geçeceği bir Axios instance oluşturup `interceptors.request` ve `interceptors.response` fonksiyonlarını tanımlıyoruz:
// src/api/http.ts
import axios, { AxiosInstance, InternalAxiosRequestConfig } from 'axios';const http: AxiosInstance = axios.create({ baseURL: process.env.NEXT_PUBLIC_API_URL || '/api', timeout: 15000, headers: { 'Content-Type': 'application/json', }, });
// Request Interceptor: Her isteğe dinamik Bearer token ekler http.interceptors.request.use( (config: InternalAxiosRequestConfig) => { if (typeof window !== 'undefined') { const token = localStorage.getItem('access_token'); if (token) { config.headers.Authorization = `Bearer ${token}`; } } return config; }, (error) => Promise.reject(error) );
// Response Interceptor: 401 ve global hataları yakalar http.interceptors.response.use( (response) => response.data, (error) => { if (error.response?.status === 401) { if (typeof window !== 'undefined') { localStorage.removeItem('access_token'); window.location.href = '/login'; } } return Promise.reject(error); } );
export default http; ```
2. Modüler Servis Katmanının Oluşturulması
İstekleri iş mantığına göre modüllere ayırmak, API değişikliklerinde yalnızca ilgili servis dosyasını güncellemeyi sağlar:
// src/services/userService.ts
import http from '@/api/http';
import { User, LoginPayload, AuthResponse } from '@/types/user';export const userService = { login: (payload: LoginPayload): Promise<AuthResponse> => { return http.post('/user/login', payload); }, getProfile: (): Promise<User> => { return http.get('/user/me'); }, updateProfile: (data: Partial<User>): Promise<User> => { return http.patch('/user/me', data); }, }; ```
3. Bileşenlerde Temiz Kullanım
Artık React bileşenleri doğrudan HTTP konfigürasyonunu bilmek zorunda kalmaz:
import { useState, useEffect } from 'react';
import { userService } from '@/services/userService';
import { User } from '@/types/user';export default function UserProfile() { const [user, setUser] = useState<User | null>(null);
useEffect(() => { userService.getProfile() .then((data) => setUser(data)) .catch((err) => console.error('Profil yüklenemedi:', err)); }, []);
if (!user) return <div>Yükleniyor...</div>;
return <div>Hoş geldiniz, {user.name}!</div>; } ```