Skip to main content

Vue.js Axios Integration

Introduction

When building modern web applications with Vue.js, communicating with APIs and servers is a crucial requirement. Axios is a popular promise-based HTTP client that makes it simple to send asynchronous HTTP requests to REST endpoints and perform CRUD operations.

In this tutorial, you'll learn how to integrate Axios with your Vue.js application to handle HTTP requests elegantly. We'll cover installation, basic setup, making different types of requests, handling responses, managing errors, and creating reusable API services.

Why Use Axios with Vue.js?

While browsers provide the native fetch API, Axios offers several advantages:

  • Automatic JSON data transformation
  • Request and response interception
  • Better error handling
  • Request cancellation
  • Wider browser support (including older browsers through polyfills)
  • Easy request timeout configuration

Setting Up Axios in Vue.js

Installation

First, let's install Axios in your Vue.js project:

bash
npm install axios
# or
yarn add axios

Basic Usage

You can use Axios directly in your Vue components by importing it:

javascript
import axios from 'axios';

export default {
name: 'UserProfile',
data() {
return {
userInfo: null,
loading: false,
error: null
};
},
mounted() {
this.fetchUserData();
},
methods: {
async fetchUserData() {
this.loading = true;
try {
const response = await axios.get('https://api.example.com/users/1');
this.userInfo = response.data;
this.error = null;
} catch (err) {
this.error = 'Failed to fetch user data';
console.error(err);
} finally {
this.loading = false;
}
}
}
}

Creating a Custom Axios Instance

For larger applications, it's recommended to create a custom Axios instance with predefined configuration:

javascript
// api/axios.js
import axios from 'axios';

const apiClient = axios.create({
baseURL: 'https://api.example.com',
timeout: 10000, // 10 seconds
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json'
}
});

export default apiClient;

Then, import this custom instance in your components:

javascript
import apiClient from '@/api/axios';

export default {
methods: {
async fetchPosts() {
try {
const response = await apiClient.get('/posts');
return response.data;
} catch (error) {
throw error;
}
}
}
}

Making Different Types of HTTP Requests

GET Request

Used to retrieve data from the server:

javascript
// GET request example
async fetchUsers() {
try {
// Get list of users with query parameters
const response = await axios.get('https://api.example.com/users', {
params: {
page: 1,
limit: 10,
sortBy: 'name'
}
});

console.log('Users data:', response.data);
return response.data;
} catch (error) {
console.error('Error fetching users:', error);
throw error;
}
}

POST Request

Used to send data to create resources on the server:

javascript
// POST request example
async createUser(userData) {
try {
const response = await axios.post('https://api.example.com/users', userData);

console.log('User created:', response.data);
return response.data;
} catch (error) {
console.error('Error creating user:', error);
throw error;
}
}

PUT Request

Used to update an entire resource:

javascript
// PUT request example
async updateUser(userId, userData) {
try {
const response = await axios.put(`https://api.example.com/users/${userId}`, userData);

console.log('User updated:', response.data);
return response.data;
} catch (error) {
console.error('Error updating user:', error);
throw error;
}
}

PATCH Request

Used to update part of a resource:

javascript
// PATCH request example
async updateUserEmail(userId, email) {
try {
const response = await axios.patch(`https://api.example.com/users/${userId}`, { email });

console.log('User email updated:', response.data);
return response.data;
} catch (error) {
console.error('Error updating user email:', error);
throw error;
}
}

DELETE Request

Used to delete resources:

javascript
// DELETE request example
async deleteUser(userId) {
try {
const response = await axios.delete(`https://api.example.com/users/${userId}`);

console.log('User deleted, server responded with:', response.data);
return response.data;
} catch (error) {
console.error('Error deleting user:', error);
throw error;
}
}

Error Handling with Axios

Proper error handling is crucial when making HTTP requests. Here's how to implement comprehensive error handling:

javascript
async fetchData() {
try {
const response = await axios.get('/api/data');
return response.data;
} catch (error) {
if (error.response) {
// The request was made and the server responded with a status code
// that falls out of the range of 2xx
console.error('Server Error:', error.response.status);
console.error('Error Data:', error.response.data);

// Handle specific status codes
if (error.response.status === 401) {
// Unauthorized, redirect to login
this.$router.push('/login');
} else if (error.response.status === 404) {
// Resource not found
this.notFound = true;
}
} else if (error.request) {
// The request was made but no response was received
console.error('No response received:', error.request);
this.networkError = true;
} else {
// Something happened in setting up the request
console.error('Request Error:', error.message);
}

throw error; // Rethrow or handle as needed
}
}

Request and Response Interceptors

Interceptors allow you to handle common tasks for all requests or responses globally:

javascript
// api/axios.js
import axios from 'axios';

const apiClient = axios.create({
baseURL: 'https://api.example.com',
timeout: 10000
});

// Add a request interceptor
apiClient.interceptors.request.use(
config => {
// Add authorization token
const token = localStorage.getItem('token');
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}

// Add timestamp to prevent caching
config.params = {
...config.params,
_t: Date.now()
};

return config;
},
error => {
// Do something with request error
return Promise.reject(error);
}
);

// Add a response interceptor
apiClient.interceptors.response.use(
response => {
// Any status code that lie within the range of 2xx
return response;
},
error => {
// Any status codes that falls outside the range of 2xx
// Global error handling logic
if (error.response && error.response.status === 401) {
// Unauthorized, clear token and redirect to login
localStorage.removeItem('token');
window.location = '/login';
}

return Promise.reject(error);
}
);

export default apiClient;

Practical Example: Building a Todo App

Let's put our knowledge into practice by building a simple Todo application that communicates with a REST API:

First, create an API service:

javascript
// services/todoService.js
import axios from 'axios';

const apiClient = axios.create({
baseURL: 'https://jsonplaceholder.typicode.com',
timeout: 10000,
headers: {
'Content-Type': 'application/json'
}
});

export default {
getTodos() {
return apiClient.get('/todos?_limit=10');
},

getTodoById(id) {
return apiClient.get(`/todos/${id}`);
},

createTodo(todo) {
return apiClient.post('/todos', todo);
},

updateTodo(id, todo) {
return apiClient.put(`/todos/${id}`, todo);
},

deleteTodo(id) {
return apiClient.delete(`/todos/${id}`);
}
};

Now, create a Vue component:

html
<template>
<div class="todo-app">
<h1>Todo List</h1>

<!-- Loading state -->
<div v-if="loading" class="loading">Loading todos...</div>

<!-- Error state -->
<div v-if="error" class="error">{{ error }}</div>

<!-- Todo list -->
<ul v-if="!loading && !error" class="todo-list">
<li v-for="todo in todos" :key="todo.id" :class="{ completed: todo.completed }">
<input type="checkbox" v-model="todo.completed" @change="updateTodo(todo)">
<span>{{ todo.title }}</span>
<button @click="removeTodo(todo.id)">Delete</button>
</li>
</ul>

<!-- Add new todo form -->
<div class="add-todo">
<input
v-model="newTodo"
placeholder="Add a new todo..."
@keyup.enter="addTodo"
/>
<button @click="addTodo">Add</button>
</div>
</div>
</template>

<script>
import todoService from '@/services/todoService';

export default {
name: 'TodoApp',
data() {
return {
todos: [],
newTodo: '',
loading: false,
error: null
};
},
created() {
this.fetchTodos();
},
methods: {
async fetchTodos() {
this.loading = true;
this.error = null;

try {
const response = await todoService.getTodos();
this.todos = response.data;
} catch (err) {
this.error = 'Failed to fetch todos. Please try again later.';
console.error('Error fetching todos:', err);
} finally {
this.loading = false;
}
},

async addTodo() {
if (!this.newTodo.trim()) return;

try {
const response = await todoService.createTodo({
title: this.newTodo,
completed: false
});

// In a real app, the API would return the new todo with an ID
// For JSONPlaceholder, we'll just add it to our local state
this.todos.unshift(response.data);
this.newTodo = '';
} catch (err) {
alert('Failed to add todo');
console.error('Error adding todo:', err);
}
},

async updateTodo(todo) {
try {
await todoService.updateTodo(todo.id, todo);
// Success feedback could be added here
} catch (err) {
alert('Failed to update todo');
console.error('Error updating todo:', err);
// Revert the change in the UI
todo.completed = !todo.completed;
}
},

async removeTodo(id) {
try {
await todoService.deleteTodo(id);
// Remove from local state
this.todos = this.todos.filter(todo => todo.id !== id);
} catch (err) {
alert('Failed to delete todo');
console.error('Error deleting todo:', err);
}
}
}
}
</script>

<style scoped>
.todo-list {
list-style-type: none;
padding: 0;
}

.completed {
text-decoration: line-through;
color: gray;
}

.loading, .error {
padding: 10px;
margin: 10px 0;
}

.error {
color: red;
border: 1px solid red;
}

.add-todo {
margin-top: 20px;
}
</style>

Canceling Requests

Sometimes you need to cancel requests, for example when a user navigates away from a page before a request completes:

javascript
import axios from 'axios';

export default {
data() {
return {
source: null,
data: null
};
},
methods: {
fetchData() {
// Cancel previous request if it exists
if (this.source) {
this.source.cancel('Operation canceled due to new request.');
}

// Create a new cancel token
this.source = axios.CancelToken.source();

// Make the request with the cancel token
axios.get('/api/data', {
cancelToken: this.source.token
})
.then(response => {
this.data = response.data;
})
.catch(error => {
if (axios.isCancel(error)) {
console.log('Request canceled:', error.message);
} else {
console.error('Error:', error);
}
});
}
},
beforeUnmount() {
// Cancel request when component is unmounted
if (this.source) {
this.source.cancel('Component unmounted');
}
}
}

Uploading Files with Axios

To upload files with Axios:

javascript
async uploadFile(file) {
const formData = new FormData();
formData.append('file', file);

try {
const response = await axios.post('/api/upload', formData, {
headers: {
'Content-Type': 'multipart/form-data'
},
onUploadProgress: progressEvent => {
const percentCompleted = Math.round(
(progressEvent.loaded * 100) / progressEvent.total
);
console.log('Upload progress:', percentCompleted);
this.uploadProgress = percentCompleted;
}
});

console.log('Upload successful:', response.data);
return response.data;
} catch (error) {
console.error('Upload failed:', error);
throw error;
}
}

Summary

In this tutorial, we've learned:

  1. How to install and set up Axios in a Vue.js application
  2. Creating custom Axios instances with predefined configuration
  3. Making various types of HTTP requests (GET, POST, PUT, PATCH, DELETE)
  4. Implementing effective error handling
  5. Using request and response interceptors
  6. Building a practical Todo application with API integration
  7. Advanced scenarios like canceling requests and uploading files

Mastering Axios in Vue.js allows you to efficiently communicate with backend services, handle responses and errors gracefully, and build robust applications that interact with remote data.

Exercises

  1. Basic API Integration: Create a Vue component that fetches and displays a list of users from the JSONPlaceholder API.

  2. Create-Read-Update-Delete: Build a complete CRUD application for managing posts, with forms for creating and editing, and confirm dialogs for deletion.

  3. API Service Layer: Refactor an existing Vue application to use a dedicated API service layer using custom Axios instances.

  4. Authentication Flow: Implement a login system that stores authentication tokens and includes them in subsequent API requests using Axios interceptors.

  5. Error Handling: Create a global error handling system that shows appropriate messages to users based on different HTTP error codes.

Additional Resources

With Axios and Vue.js, you have a powerful combination for building applications that communicate effectively with backend services. As you practice and build more complex applications, you'll develop a deeper understanding of HTTP communications and API integration patterns.



If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)