Chapter 33: Vue HTTP Requests
Vue HTTP Requests (how to fetch data from APIs / backends / servers)
In 2026 almost no serious Vue app works only with local/static data. You need to talk to a server — get users, products, posts, orders, weather, auth tokens, etc.
Vue itself does not include any built-in HTTP client (unlike Angular with HttpClient). You have to choose and integrate one yourself.
Current Best Choices in February 2026 (Hyderabad / India scene)
| Library | Size (min+gzip) | Bundle impact | TypeScript support | Popularity 2026 | Recommendation for new projects |
|---|---|---|---|---|---|
| fetch (native) | 0 KB | None | Excellent | Very high | ★★★★☆ (if simple) |
| axios | ~13–15 KB | Small | Excellent | Still #1 | ★★★★★ (most teams) |
| ky | ~2–3 KB | Tiny | Excellent | Growing fast | ★★★★☆ (modern & lightweight) |
| ofetch (unjs) | ~3 KB | Tiny | Excellent | Very high (Nuxt) | ★★★★★ (especially Nuxt) |
| TanStack Query / Vue Query | ~15–25 KB | Medium | Excellent | Exploding | ★★★★★ (data fetching + cache) |
What most experienced developers choose in 2026:
- Small/simple app or static site → native fetch
- Medium/large SPA → axios (still king for reliability & ecosystem) or ofetch
- App with lots of data fetching, caching, optimistic updates, pagination → TanStack Query (@tanstack/vue-query)
- Using Nuxt 3 → ofetch (built-in, auto-typed, great DX)
Today I’ll teach you both axios and native fetch (the two most common), then show how TanStack Query changes everything.
1. Classic & Most Common: Axios
Install
|
0 1 2 3 4 5 6 |
npm install axios |
Basic GET Request – User Profile
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 |
<!-- src/views/ProfileView.vue --> <template> <div class="profile"> <h1>User Profile</h1> <div v-if="loading" class="loading">Loading...</div> <div v-else-if="error" class="error"> {{ error }} <button @click="fetchUser">Try Again</button> </div> <div v-else-if="user" class="user-card"> <img :src="user.avatar" alt="Avatar" class="avatar" /> <h2>{{ user.name }}</h2> <p>{{ user.email }}</p> <p>Joined: {{ formatDate(user.createdAt) }}</p> </div> </div> </template> <script setup lang="ts"> import { ref, onMounted } from 'vue' import axios from 'axios' interface User { id: number name: string email: string avatar: string createdAt: string } const user = ref<User | null>(null) const loading = ref(true) const error = ref<string | null>(null) async function fetchUser() { loading.value = true error.value = null try { const response = await axios.get<User>('https://api.example.com/users/123', { headers: { Authorization: `Bearer ${localStorage.getItem('token')}` } }) user.value = response.data } catch (err: any) { error.value = err.response?.data?.message || 'Failed to load profile' console.error(err) } finally { loading.value = false } } function formatDate(dateStr: string) { return new Date(dateStr).toLocaleDateString('en-IN', { day: 'numeric', month: 'long', year: 'numeric' }) } onMounted(fetchUser) </script> <style scoped> .user-card { padding: 2rem; background: white; border-radius: 12px; box-shadow: 0 4px 15px rgba(0,0,0,0.08); text-align: center; max-width: 400px; margin: 2rem auto; } .avatar { width: 120px; height: 120px; border-radius: 50%; object-fit: cover; margin-bottom: 1rem; } .loading, .error { text-align: center; padding: 4rem; } .error button { margin-top: 1rem; padding: 0.8rem 1.5rem; } </style> |
Axios Advantages (why many teams still love it in 2026)
- Automatic JSON parsing
- Request & response interceptors (great for auth tokens, logging, error handling)
- Cancel tokens / AbortController support
- Built-in progress events
- Very mature TypeScript types
- Huge ecosystem (axios-retry, axios-cache-adapter…)
Global Axios Setup (common in real apps)
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
// src/utils/axios.ts import axios from 'axios' const api = axios.create({ baseURL: import.meta.env.VITE_API_URL || 'https://api.example.com', timeout: 10000, headers: { 'Content-Type': 'application/json' } }) // Add auth token automatically api.interceptors.request.use(config => { const token = localStorage.getItem('token') if (token) { config.headers.Authorization = `Bearer ${token}` } return config }) // Handle 401 globally api.interceptors.response.use( response => response, error => { if (error.response?.status === 401) { localStorage.removeItem('token') window.location.href = '/login' } return Promise.reject(error) } ) export default api |
Then use:
|
0 1 2 3 4 5 6 7 8 |
import api from '@/utils/axios' const response = await api.get('/users/123') |
2. Native fetch (No Dependencies – Very Popular in 2026)
Same example with fetch
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
async function fetchUser() { loading.value = true error.value = null try { const token = localStorage.getItem('token') const response = await fetch('https://api.example.com/users/123', { headers: { 'Authorization': token ? `Bearer ${token}` : '', 'Accept': 'application/json' } }) if (!response.ok) { throw new Error(`HTTP error! status: {response.status}`) } user.value = await response.json() } catch (err: any) { error.value = err.message || 'Failed to load profile' } finally { loading.value = false } } |
Pros of native fetch in 2026
- 0 KB added to bundle
- Built-in AbortController support
- Streaming support
- Native Promise API
- Works perfectly with async/await
Cons
- No automatic JSON parsing → response.json()
- No built-in interceptors → you need to wrap it
- Error handling more manual
Many teams now use ofetch (from unjs) — tiny wrapper over fetch with axios-like DX.
|
0 1 2 3 4 5 6 |
npm install ofetch |
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
import { ofetch } from 'ofetch' const api = ofetch.create({ baseURL: 'https://api.example.com', headers: { Accept: 'application/json' }, onRequest({ options }) { const token = localStorage.getItem('token') if (token) options.headers.Authorization = `Bearer ${token}` }, onResponseError({ response }) { if (response.status === 401) { // logout } } }) const user = await api<User>('/users/123') |
3. TanStack Query (Vue Query) – The 2026 Game Changer
When your app has lots of data fetching, caching, pagination, optimistic updates → raw axios/fetch becomes painful.
Install
|
0 1 2 3 4 5 6 |
npm install @tanstack/vue-query |
Setup in main.ts
|
0 1 2 3 4 5 6 7 8 9 10 11 12 |
import { createApp } from 'vue' import { VueQueryPlugin } from '@tanstack/vue-query' import App from './App.vue' const app = createApp(App) app.use(VueQueryPlugin) app.mount('#app') |
Usage – Auto caching, background refetch, loading states
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
<script setup lang="ts"> import { useQuery } from '@tanstack/vue-query' import axios from 'axios' const { data: user, isLoading, error } = useQuery({ queryKey: ['user', 123], queryFn: () => axios.get('/users/123').then(res => res.data), staleTime: 5 * 60 * 1000 // 5 minutes }) </script> <template> <div v-if="isLoading">Loading profile...</div> <div v-else-if="error">{{ error.message }}</div> <div v-else> <h2>{{ user.name }}</h2> <p>{{ user.email }}</p> </div> </template> |
→ Automatic caching, refetch on window focus, deduping, retries, etc.
Summary – Which HTTP Client to Choose in 2026?
| App Type | Recommended Choice | Why |
|---|---|---|
| Small / static site | native fetch | Zero bundle cost |
| Medium SPA | axios or ofetch | Good DX, interceptors |
| Large app, lots of data | TanStack Query + axios/ofetch/fetch | Caching, optimistic UI, background sync |
| Nuxt 3 project | ofetch (built-in) | Auto-typed, nitro integration |
| Need cancel / progress | axios | Mature features |
Your Mini Homework
- Create a UserProfile.vue component
- Fetch user data from https://jsonplaceholder.typicode.com/users/1 using axios and fetch
- Add loading & error states
- (Bonus) Add a button “Refresh” that refetches
Any part confusing? Want me to show:
- Axios interceptors full auth flow?
- TanStack Query pagination / infinite scroll?
- ofetch vs axios comparison?
- Upload file with progress bar?
Just tell me — we’ll build the next real API example together 🚀
Happy fetching from Hyderabad! 💙
