HTTP Requests
Vue.js Development
HTTP Requests in Vue
Most Vue applications need to communicate with a back-end API to fetch, create, update, and delete data. Vue does not include a built-in HTTP client, giving you the freedom to use the browser's native Fetch API or popular libraries like Axios. Regardless of which HTTP client you choose, the pattern for integrating API calls with Vue components is consistent: trigger requests from lifecycle hooks or user actions, manage loading and error states reactively, and update the component's data with the response. Properly handling async operations with clear state feedback creates a smooth user experience.
Using the Fetch API
The Fetch API is built into modern browsers and requires no additional dependencies. It returns promises and works naturally with async/await syntax.
<template>
<div>
<div v-if="loading" class="spinner">Loading...</div>
<div v-else-if="error" class="error">
<p>Error: {{ error }}</p>
<button @click="fetchPosts">Retry</button>
</div>
<div v-else>
<h2>Posts ({{ posts.length }})</h2>
<div v-for="post in posts" :key="post.id" class="post">
<h3>{{ post.title }}</h3>
<p>{{ post.body }}</p>
</div>
</div>
</div>
</template>
<script>
export default {
data() {
return {
posts: [],
loading: false,
error: null,
};
},
created() {
this.fetchPosts();
},
methods: {
async fetchPosts() {
this.loading = true;
this.error = null;
try {
const response = await fetch(
"https://jsonplaceholder.typicode.com/posts?_limit=10"
);
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
this.posts = await response.json();
} catch (e) {
this.error = e.message;
} finally {
this.loading = false;
}
},
async createPost(title, body) {
try {
const response = await fetch(
"https://jsonplaceholder.typicode.com/posts",
{
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ title, body, userId: 1 }),
}
);
const newPost = await response.json();
this.posts.unshift(newPost);
} catch (e) {
this.error = e.message;
}
},
async deletePost(id) {
try {
await fetch(`https://jsonplaceholder.typicode.com/posts/${id}`, {
method: "DELETE",
});
this.posts = this.posts.filter((p) => p.id !== id);
} catch (e) {
this.error = e.message;
}
},
},
};
</script>
Using Axios
Axios is a popular HTTP client with features like request/response interceptors, automatic JSON parsing, request cancellation, and better error handling for HTTP status codes.
// Install: npm install axios
// api/client.js — create a configured Axios instance
import axios from "axios";
const apiClient = axios.create({
baseURL: "https://api.example.com",
timeout: 10000,
headers: { "Content-Type": "application/json" },
});
// Request interceptor — add auth token
apiClient.interceptors.request.use((config) => {
const token = localStorage.getItem("token");
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
});
// Response interceptor — handle common errors
apiClient.interceptors.response.use(
(response) => response,
(error) => {
if (error.response?.status === 401) {
localStorage.removeItem("token");
window.location.href = "/login";
}
return Promise.reject(error);
}
);
export default apiClient;
// Usage in a component
import apiClient from "../api/client";
export default {
data() {
return { users: [], loading: false, error: null };
},
async created() {
this.loading = true;
try {
const { data } = await apiClient.get("/users");
this.users = data;
} catch (e) {
this.error = e.response?.data?.message || e.message;
} finally {
this.loading = false;
}
},
};
Composable for API Calls
// composables/useApi.js
import { ref } from "vue";
export function useApi(apiCall) {
const data = ref(null);
const error = ref(null);
const loading = ref(false);
async function execute(...args) {
loading.value = true;
error.value = null;
try {
const result = await apiCall(...args);
data.value = result;
return result;
} catch (e) {
error.value = e.message;
throw e;
} finally {
loading.value = false;
}
}
return { data, error, loading, execute };
}
// Usage:
// const { data: users, loading, error, execute: fetchUsers } = useApi(
// () => fetch("/api/users").then(r => r.json())
// );
// onMounted(() => fetchUsers());
Tip: Create a centralized API client with interceptors for authentication, error handling, and base URL configuration. This avoids duplicating headers and error handling in every component that makes API calls.
Key Takeaways
- Use the native Fetch API or Axios for HTTP requests — Vue does not include a built-in client.
- Always manage
loading,error, anddatastates for every async operation. - Create a centralized API client with interceptors for auth tokens and error handling.
- Extract API logic into composable functions for reuse across components.
- Handle errors gracefully with retry buttons, error messages, and fallback content.