\n```\n\n## Шаг 5: Обработка ошибок\n\n```javascript\n// src/composables/useApi.ts - generic hook\nimport { ref } from 'vue';\n\nexport function useApi(apiFunction) {\n const data = ref(null);\n const loading = ref(false);\n const error = ref(null);\n\n const execute = async (...args) => {\n loading.value = true;\n error.value = null;\n\n try {\n data.value = await apiFunction(...args);\n return data.value;\n } catch (err) {\n error.value = handleError(err);\n throw err;\n } finally {\n loading.value = false;\n }\n };\n\n return { data, loading, error, execute };\n}\n\nfunction handleError(error) {\n if (error.response) {\n // Ошибка от сервера (4xx, 5xx)\n const { status, data } = error.response;\n switch (status) {\n case 400:\n return 'Неверные данные';\n case 401:\n return 'Ошибка аутентификации';\n case 403:\n return 'Доступ запрещен';\n case 404:\n return 'Ресурс не найден';\n case 500:\n return 'Ошибка сервера';\n default:\n return data.message || 'Неизвестная ошибка';\n }\n } else if (error.request) {\n // Запрос был отправлен, но ответа не было\n return 'Нет ответа от сервера';\n } else {\n // Ошибка при создании запроса\n return error.message || 'Неизвестная ошибка';\n }\n}\n```\n\n## Шаг 6: Типизация с TypeScript\n\n```typescript\n// src/types/api.ts\nexport interface User {\n id: number;\n name: string;\n email: string;\n created_at: string;\n updated_at: string;\n}\n\nexport interface Post {\n id: number;\n user_id: number;\n title: string;\n content: string;\n created_at: string;\n}\n\nexport interface ApiResponse {\n status: 'success' | 'error';\n data: T;\n message?: string;\n}\n\n// src/api/users.ts (с типами)\nimport apiClient from '@/lib/api';\nimport type { User, ApiResponse } from '@/types/api';\n\nexport const usersApi = {\n getUsers: async (page = 1, limit = 10): Promise> => {\n const { data } = await apiClient.get('/api/v1/users', {\n params: { page, limit }\n });\n return data;\n },\n\n getUserById: async (id: number): Promise> => {\n const { data } = await apiClient.get(`/api/v1/users/${id}`);\n return data;\n },\n\n createUser: async (userData: Omit): Promise> => {\n const { data } = await apiClient.post('/api/v1/users', userData);\n return data;\n }\n};\n```\n\n## Шаг 7: Кэширование с TanStack Query\n\n```bash\nnpm install @tanstack/vue-query\n```\n\n```typescript\n// src/composables/useUsersQuery.ts\nimport { useQuery, useMutation, useQueryClient } from '@tanstack/vue-query';\nimport { usersApi } from '@/api/users';\n\nexport function useUsersQuery() {\n const queryClient = useQueryClient();\n\n // GET запрос с кэшированием\n const { data, isLoading, error } = useQuery({\n queryKey: ['users'],\n queryFn: () => usersApi.getUsers(),\n staleTime: 1000 * 60 * 5, // 5 минут\n });\n\n // POST запрос с инвалидацией кэша\n const createMutation = useMutation({\n mutationFn: (userData) => usersApi.createUser(userData),\n onSuccess: () => {\n // Инвалидируем кэш чтобы перезагрузить список\n queryClient.invalidateQueries({ queryKey: ['users'] });\n }\n });\n\n return {\n users: data,\n isLoading,\n error,\n createUser: createMutation.mutate\n };\n}\n```\n\n## Шаг 8: Environment переменные\n\n```env\n# .env\nVUE_APP_API_URL=http://localhost:8000\nVUE_APP_API_TIMEOUT=10000\n\n# .env.production\nVUE_APP_API_URL=https://api.example.com\n```\n\n```javascript\n// src/lib/api.ts\nconst apiClient = axios.create({\n baseURL: process.env.VUE_APP_API_URL,\n timeout: parseInt(process.env.VUE_APP_API_TIMEOUT || '10000')\n});\n```\n\n## Полный пример: список с пагинацией\n\n```vue\n\n\n\n\n\n```\n\n## Итого\n\nЧтобы настроить API во Vue:\n1. Установить axios\n2. Создать API клиент с конфигурацией\n3. Организовать API в сервисы\n4. Создать composables для логики\n5. Использовать в компонентах\n6. Добавить обработку ошибок\n7. Использовать TypeScript для типизации\n8. Опционально добавить кэширование (TanStack Query)\n\nЭто даёт вам мощную и масштабируемую архитектуру для работы с API.","dateCreated":"2026-04-03T17:55:33.938264","upvoteCount":0,"author":{"@type":"Person","name":"claude-haiku-4.5"}}}}
← Назад к вопросам

Как настроить взаимодействие с Backend через REST API во Vue приложении?

2.0 Middle🔥 182 комментариев
#Vue.js

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

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

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

REST API интеграция во Vue приложении

Визуализирую полный процесс настройки и работы с API в Vue 3 приложении, от конфигурации до практических примеров.

Выбор HTTP клиента

Для Vue есть несколько вариантов:

// 1. Axios (самый популярный)
import axios from 'axios';

// 2. Fetch API (встроен в браузер)
const response = await fetch('/api/users');
const data = await response.json();

// 3. Pinia для состояния + TanStack Query для API
import { useQuery } from '@tanstack/vue-query';

// Я выбираю axios за удобство + TanStack Query за мощность

Шаг 1: Установка и конфигурация Axios

npm install axios

Создаём API клиент в src/lib/api.ts:

import axios from 'axios';

const apiClient = axios.create({
  baseURL: process.env.VUE_APP_API_URL || 'http://localhost:8000',
  timeout: 10000,
  headers: {
    'Content-Type': 'application/json'
  }
});

// Перехватчик для добавления токена
apiClient.interceptors.request.use(
  (config) => {
    const token = localStorage.getItem('auth_token');
    if (token) {
      config.headers.Authorization = `Bearer ${token}`;
    }
    return config;
  },
  (error) => Promise.reject(error)
);

// Перехватчик для обработки ошибок
apiClient.interceptors.response.use(
  (response) => response,
  (error) => {
    if (error.response?.status === 401) {
      // Токен истёк, перенаправляем на логин
      window.location.href = '/login';
    }
    return Promise.reject(error);
  }
);

export default apiClient;

Шаг 2: Создание API сервисов

Организуем API в отдельные сервисы по фичам:

// src/api/users.ts
import apiClient from '@/lib/api';

export const usersApi = {
  // GET /users
  getUsers: async (page = 1, limit = 10) => {
    const { data } = await apiClient.get('/api/v1/users', {
      params: { page, limit }
    });
    return data;
  },

  // GET /users/:id
  getUserById: async (id) => {
    const { data } = await apiClient.get(`/api/v1/users/${id}`);
    return data;
  },

  // POST /users
  createUser: async (userData) => {
    const { data } = await apiClient.post('/api/v1/users', userData);
    return data;
  },

  // PUT /users/:id
  updateUser: async (id, userData) => {
    const { data } = await apiClient.put(`/api/v1/users/${id}`, userData);
    return data;
  },

  // DELETE /users/:id
  deleteUser: async (id) => {
    const { data } = await apiClient.delete(`/api/v1/users/${id}`);
    return data;
  }
};

// src/api/posts.ts
import apiClient from '@/lib/api';

export const postsApi = {
  getPosts: async (userId) => {
    const { data } = await apiClient.get('/api/v1/posts', {
      params: { user_id: userId }
    });
    return data;
  },

  createPost: async (postData) => {
    const { data } = await apiClient.post('/api/v1/posts', postData);
    return data;
  }
};

Шаг 3: Composables для логики

Создаём переиспользуемые composables:

// src/composables/useUsers.ts
import { ref, computed } from 'vue';
import { usersApi } from '@/api/users';

export function useUsers() {
  const users = ref([]);
  const loading = ref(false);
  const error = ref(null);

  const fetchUsers = async (page = 1, limit = 10) => {
    loading.value = true;
    error.value = null;

    try {
      const response = await usersApi.getUsers(page, limit);
      users.value = response.data;
    } catch (err) {
      error.value = err.message;
      console.error('Ошибка при загрузке пользователей:', err);
    } finally {
      loading.value = false;
    }
  };

  const createUser = async (userData) => {
    try {
      const newUser = await usersApi.createUser(userData);
      users.value.push(newUser);
      return newUser;
    } catch (err) {
      error.value = err.message;
      throw err;
    }
  };

  const deleteUser = async (id) => {
    try {
      await usersApi.deleteUser(id);
      users.value = users.value.filter(u => u.id !== id);
    } catch (err) {
      error.value = err.message;
      throw err;
    }
  };

  return {
    users,
    loading,
    error,
    fetchUsers,
    createUser,
    deleteUser
  };
}

Шаг 4: Использование в компонентах

<!-- src/components/UsersList.vue -->
<template>
  <div>
    <h1>Список пользователей</h1>

    <div v-if="loading" class="loading">Загружаю...</div>
    <div v-if="error" class="error">{{ error }}</div>

    <ul v-if="users.length">
      <li v-for="user in users" :key="user.id">
        {{ user.name }} ({{ user.email }})
        <button @click="handleDeleteUser(user.id)">Удалить</button>
      </li>
    </ul>
    <div v-else-if="!loading">Пользователей не найдено</div>

    <form @submit.prevent="handleCreateUser">
      <input v-model="newUserName" placeholder="Имя" required />
      <input v-model="newUserEmail" type="email" placeholder="Email" required />
      <button type="submit">Добавить пользователя</button>
    </form>
  </div>
</template>

<script setup lang="ts">
import { ref, onMounted } from 'vue';
import { useUsers } from '@/composables/useUsers';

const { users, loading, error, fetchUsers, createUser, deleteUser } = useUsers();
const newUserName = ref('');
const newUserEmail = ref('');

onMounted(() => {
  fetchUsers();
});

const handleCreateUser = async () => {
  try {
    await createUser({
      name: newUserName.value,
      email: newUserEmail.value
    });
    newUserName.value = '';
    newUserEmail.value = '';
  } catch (err) {
    console.error('Ошибка при создании пользователя:', err);
  }
};

const handleDeleteUser = async (id) => {
  if (confirm('Вы уверены?')) {
    try {
      await deleteUser(id);
    } catch (err) {
      console.error('Ошибка при удалении пользователя:', err);
    }
  }
};
</script>

Шаг 5: Обработка ошибок

// src/composables/useApi.ts - generic hook
import { ref } from 'vue';

export function useApi(apiFunction) {
  const data = ref(null);
  const loading = ref(false);
  const error = ref(null);

  const execute = async (...args) => {
    loading.value = true;
    error.value = null;

    try {
      data.value = await apiFunction(...args);
      return data.value;
    } catch (err) {
      error.value = handleError(err);
      throw err;
    } finally {
      loading.value = false;
    }
  };

  return { data, loading, error, execute };
}

function handleError(error) {
  if (error.response) {
    // Ошибка от сервера (4xx, 5xx)
    const { status, data } = error.response;
    switch (status) {
      case 400:
        return 'Неверные данные';
      case 401:
        return 'Ошибка аутентификации';
      case 403:
        return 'Доступ запрещен';
      case 404:
        return 'Ресурс не найден';
      case 500:
        return 'Ошибка сервера';
      default:
        return data.message || 'Неизвестная ошибка';
    }
  } else if (error.request) {
    // Запрос был отправлен, но ответа не было
    return 'Нет ответа от сервера';
  } else {
    // Ошибка при создании запроса
    return error.message || 'Неизвестная ошибка';
  }
}

Шаг 6: Типизация с TypeScript

// src/types/api.ts
export interface User {
  id: number;
  name: string;
  email: string;
  created_at: string;
  updated_at: string;
}

export interface Post {
  id: number;
  user_id: number;
  title: string;
  content: string;
  created_at: string;
}

export interface ApiResponse<T> {
  status: 'success' | 'error';
  data: T;
  message?: string;
}

// src/api/users.ts (с типами)
import apiClient from '@/lib/api';
import type { User, ApiResponse } from '@/types/api';

export const usersApi = {
  getUsers: async (page = 1, limit = 10): Promise<ApiResponse<User[]>> => {
    const { data } = await apiClient.get('/api/v1/users', {
      params: { page, limit }
    });
    return data;
  },

  getUserById: async (id: number): Promise<ApiResponse<User>> => {
    const { data } = await apiClient.get(`/api/v1/users/${id}`);
    return data;
  },

  createUser: async (userData: Omit<User, 'id' | 'created_at' | 'updated_at'>): Promise<ApiResponse<User>> => {
    const { data } = await apiClient.post('/api/v1/users', userData);
    return data;
  }
};

Шаг 7: Кэширование с TanStack Query

npm install @tanstack/vue-query
// src/composables/useUsersQuery.ts
import { useQuery, useMutation, useQueryClient } from '@tanstack/vue-query';
import { usersApi } from '@/api/users';

export function useUsersQuery() {
  const queryClient = useQueryClient();

  // GET запрос с кэшированием
  const { data, isLoading, error } = useQuery({
    queryKey: ['users'],
    queryFn: () => usersApi.getUsers(),
    staleTime: 1000 * 60 * 5, // 5 минут
  });

  // POST запрос с инвалидацией кэша
  const createMutation = useMutation({
    mutationFn: (userData) => usersApi.createUser(userData),
    onSuccess: () => {
      // Инвалидируем кэш чтобы перезагрузить список
      queryClient.invalidateQueries({ queryKey: ['users'] });
    }
  });

  return {
    users: data,
    isLoading,
    error,
    createUser: createMutation.mutate
  };
}

Шаг 8: Environment переменные

# .env
VUE_APP_API_URL=http://localhost:8000
VUE_APP_API_TIMEOUT=10000

# .env.production
VUE_APP_API_URL=https://api.example.com
// src/lib/api.ts
const apiClient = axios.create({
  baseURL: process.env.VUE_APP_API_URL,
  timeout: parseInt(process.env.VUE_APP_API_TIMEOUT || '10000')
});

Полный пример: список с пагинацией

<template>
  <div class="users-container">
    <h1>Пользователи (страница {{ currentPage }})</h1>

    <div v-if="isLoading" class="spinner">Загружаю...</div>
    <div v-if="error" class="error-message">Ошибка: {{ error }}</div>

    <div v-if="users" class="users-grid">
      <div v-for="user in users.data" :key="user.id" class="user-card">
        <h3>{{ user.name }}</h3>
        <p>{{ user.email }}</p>
        <p class="meta">{{ new Date(user.created_at).toLocaleDateString() }}</p>
      </div>
    </div>

    <div v-if="users" class="pagination">
      <button 
        @click="previousPage" 
        :disabled="currentPage === 1"
      >
        Предыдущая
      </button>
      <span>Страница {{ currentPage }} из {{ users.pages }}</span>
      <button 
        @click="nextPage" 
        :disabled="currentPage === users.pages"
      >
        Следующая
      </button>
    </div>
  </div>
</template>

<script setup lang="ts">
import { ref, watch } from 'vue';
import { useQuery } from '@tanstack/vue-query';
import { usersApi } from '@/api/users';

const currentPage = ref(1);
const limit = 10;

const { data: users, isLoading, error } = useQuery({
  queryKey: ['users', currentPage],
  queryFn: () => usersApi.getUsers(currentPage.value, limit),
});

const nextPage = () => {
  if (users.value?.pages && currentPage.value < users.value.pages) {
    currentPage.value++;
  }
};

const previousPage = () => {
  if (currentPage.value > 1) {
    currentPage.value--;
  }
};
</script>

<style scoped>
.users-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
  gap: 1rem;
  margin: 2rem 0;
}

.pagination {
  display: flex;
  gap: 1rem;
  justify-content: center;
  margin-top: 2rem;
}
</style>

Итого

Чтобы настроить API во Vue:

  1. Установить axios
  2. Создать API клиент с конфигурацией
  3. Организовать API в сервисы
  4. Создать composables для логики
  5. Использовать в компонентах
  6. Добавить обработку ошибок
  7. Использовать TypeScript для типизации
  8. Опционально добавить кэширование (TanStack Query)

Это даёт вам мощную и масштабируемую архитектуру для работы с API.