← Назад к вопросам

Как делаешь запросы на сервер?

1.3 Junior🔥 81 комментариев
#Браузер и сетевые технологии

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI2 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

HTTP запросы с фронтенда: подходы и best practices

Это фундаментальная часть фронтенд разработки. Расскажу о разных подходах и их плюсах/минусах в зависимости от ситуации.

1. Fetch API (современный стандарт)

Это встроенный в браузер API для HTTP запросов:

// Базовый GET запрос
const response = await fetch('https://api.example.com/users');
const data = await response.json();
console.log(data);

// С обработкой ошибок
const fetchUsers = async () => {
  try {
    const response = await fetch('/api/users', {
      method: 'GET',
      headers: {
        'Content-Type': 'application/json',
      },
    });
    
    if (!response.ok) {
      throw new Error(`HTTP error! status: ${response.status}`);
    }
    
    const data = await response.json();
    return data;
  } catch (error) {
    console.error('Fetch error:', error);
    throw error; // Пробросить выше
  }
};

// POST с данными
const createUser = async (userData) => {
  const response = await fetch('/api/users', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
    },
    body: JSON.stringify(userData),
  });
  
  if (!response.ok) throw new Error('Failed to create user');
  return response.json();
};

// DELETE
const deleteUser = async (userId) => {
  const response = await fetch(`/api/users/${userId}`, {
    method: 'DELETE',
  });
  
  if (!response.ok) throw new Error('Failed to delete');
};

Плюсы fetch:

  • Встроен в браузер
  • Использует Promise
  • Легко работать с JSON
  • Минимальная зависимость

Минусы fetch:

  • Нет встроенного обработки timeouts
  • Нужно вручную обрабатывать ошибки
  • Нет встроенной retry логики
  • Немного verbose синтаксис для сложных случаев

2. Axios (популярная библиотека)

Альтернатива с удобнее API:

import axios from 'axios';

// GET
const data = await axios.get('/api/users');
console.log(data.data); // Заметьте двойное .data

// POST
const response = await axios.post('/api/users', {
  name: 'John',
  email: 'john@example.com',
});

// Конфигурация по умолчанию
const api = axios.create({
  baseURL: 'https://api.example.com',
  timeout: 5000,
  headers: {
    'X-Custom-Header': 'value',
  },
});

// Использование
const users = await api.get('/users');

// Интерceptors (перехватчики)
api.interceptors.request.use(
  (config) => {
    // Добавить token перед запросом
    config.headers.Authorization = `Bearer ${getToken()}`;
    return config;
  },
  (error) => Promise.reject(error)
);

api.interceptors.response.use(
  (response) => response,
  (error) => {
    if (error.response?.status === 401) {
      // Handle unauthorized
      redirectToLogin();
    }
    return Promise.reject(error);
  }
);

Плюсы axios:

  • Удобный API
  • Встроенные timeout и retry
  • Interceptors
  • Отмена запросов (AbortController)

Минусы:

  • Дополнительная зависимость
  • Небольшой overhead

3. React-специфичные подходы

useEffect + fetch (базовый):

function UserList() {
  const [users, setUsers] = useState([]);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);
  
  useEffect(() => {
    let isMounted = true; // Для очистки
    
    const fetchUsers = async () => {
      try {
        const response = await fetch('/api/users');
        if (!response.ok) throw new Error('Failed');
        const data = await response.json();
        
        if (isMounted) setUsers(data); // Проверка!
      } catch (err) {
        if (isMounted) setError(err.message);
      } finally {
        if (isMounted) setLoading(false);
      }
    };
    
    fetchUsers();
    
    return () => {
      isMounted = false; // Очистка при unmount
    };
  }, []);
  
  if (loading) return <div>Loading...</div>;
  if (error) return <div>Error: {error}</div>;
  
  return (
    <ul>
      {users.map(user => <li key={user.id}>{user.name}</li>)}
    </ul>
  );
}

React Query / TanStack Query (рекомендуется):

Мощная библиотека для управления асинхронным состоянием:

import { useQuery, useMutation } from '@tanstack/react-query';

// Fetch данных
function UserList() {
  const { data: users, isLoading, error } = useQuery({
    queryKey: ['users'], // Cache key
    queryFn: async () => {
      const response = await fetch('/api/users');
      return response.json();
    },
  });
  
  if (isLoading) return <div>Loading...</div>;
  if (error) return <div>Error: {error.message}</div>;
  
  return (
    <ul>
      {users.map(user => <li key={user.id}>{user.name}</li>)}
    </ul>
  );
}

// Мутация (создание/обновление)
function CreateUserForm() {
  const { mutate: createUser, isPending } = useMutation({
    mutationFn: (userData) =>
      fetch('/api/users', {
        method: 'POST',
        body: JSON.stringify(userData),
      }).then(r => r.json()),
    onSuccess: (newUser) => {
      // Автоматически обновляет кеш
      queryClient.invalidateQueries({ queryKey: ['users'] });
    },
  });
  
  return (
    <form onSubmit={(e) => {
      e.preventDefault();
      createUser({ name: 'John' });
    }}>
      <button disabled={isPending}>Submit</button>
    </form>
  );
}

Плюсы React Query:

  • Автоматическое кеширование
  • Синхронизация между вкладками
  • Встроенный retry
  • Оптимистичные обновления
  • Фоновая ре-валидация

SWR (альтернатива от Vercel):

import useSWR from 'swr';

function UserList() {
  const { data: users, error } = useSWR('/api/users', fetcher);
  
  if (!data) return <div>Loading...</div>;
  if (error) return <div>Error: {error.message}</div>;
  
  return <ul>{users.map(u => <li key={u.id}>{u.name}</li>)}</ul>
}

4. GraphQL (для современных API)

Вместо REST иногда используется GraphQL:

import { ApolloClient, gql, useQuery } from '@apollo/client';

const GET_USERS = gql`
  query GetUsers {
    users {
      id
      name
      email
    }
  }
`;

function UserList() {
  const { data, loading, error } = useQuery(GET_USERS);
  
  if (loading) return <div>Loading...</div>;
  if (error) return <div>Error</div>;
  
  return (
    <ul>
      {data.users.map(u => <li key={u.id}>{u.name}</li>)}
    </ul>
  );
}

5. Best Practices

1. Абстракция API слоя:

// api/users.ts
const BASE_URL = process.env.REACT_APP_API_URL;

export const usersApi = {
  getAll: () => fetch(`${BASE_URL}/users`).then(r => r.json()),
  
  getById: (id) =>
    fetch(`${BASE_URL}/users/${id}`).then(r => r.json()),
  
  create: (data) =>
    fetch(`${BASE_URL}/users`, {
      method: 'POST',
      body: JSON.stringify(data),
    }).then(r => r.json()),
  
  update: (id, data) =>
    fetch(`${BASE_URL}/users/${id}`, {
      method: 'PUT',
      body: JSON.stringify(data),
    }).then(r => r.json()),
  
  delete: (id) =>
    fetch(`${BASE_URL}/users/${id}`, { method: 'DELETE' }),
};

// Использование
const users = await usersApi.getAll();

2. Обработка ошибок:

class ApiError extends Error {
  constructor(status, message) {
    super(message);
    this.status = status;
  }
}

const apiCall = async (url, options = {}) => {
  const response = await fetch(url, options);
  
  if (!response.ok) {
    const error = await response.json().catch(() => ({}));
    throw new ApiError(
      response.status,
      error.message || response.statusText
    );
  }
  
  return response.json();
};

3. Timeout:

const fetchWithTimeout = (url, timeout = 5000) => {
  return Promise.race([
    fetch(url),
    new Promise((_, reject) =>
      setTimeout(() => reject(new Error('Timeout')), timeout)
    ),
  ]);
};

4. Retry логика:

const retryFetch = async (url, options = {}, retries = 3) => {
  for (let i = 0; i < retries; i++) {
    try {
      return await fetch(url, options);
    } catch (error) {
      if (i === retries - 1) throw error;
      await new Promise(r => setTimeout(r, 1000 * Math.pow(2, i)));
    }
  }
};

5. Типизация (TypeScript):

interface User {
  id: number;
  name: string;
  email: string;
}

const getUsers = async (): Promise<User[]> => {
  const response = await fetch('/api/users');
  return response.json();
};

Рекомендуемый выбор инструмента

Просто и быстро: Fetch API
        ↓
Есть хардкод логики: Axios
        ↓
Нужно кешировать + синхронизировать: React Query
        ↓
Модерный API с GraphQL: Apollo Client

Итог

Мой подход в production:

  • API слой с функциями (api/users.ts, api/posts.ts)
  • React Query для управления состоянием
  • Axios с interceptors для обработки ошибок и авторизации
  • TypeScript для типобезопасности
  • Error boundaries для graceful error handling

Это обеспечивает надёжность, масштабируемость и удобство разработки.

Как делаешь запросы на сервер? | PrepBro