Skip to main content
Vue.js for Beginners to Advanced
CHAPTER 18 Beginner

API Integration and Axios

Updated: May 18, 2026
5 min read

# CHAPTER 18

API Integration and Axios

1. Chapter Introduction

Modern web applications communicate with backend APIs for data. Axios is the most popular HTTP client for Vue — it's more feature-rich than the native fetch API, with automatic JSON parsing, request interceptors, error handling, and cancellation.

2. Learning Objectives

  • Install and configure Axios.
  • Make GET, POST, PUT, DELETE requests.
  • Handle loading and error states.
  • Use Axios interceptors for global behavior.
  • Build a weather application.

3. Installation and Setup

bash
1
npm install axios
javascript
1234567891011121314151617181920212223242526272829303132333435
// src/api/axios.js — Configured Axios instance
import axios from 'axios'

const api = axios.create({
  baseURL: import.meta.env.VITE_API_BASE_URL || 'https://api.example.com',
  timeout: 10000,
  headers: {
    'Content-Type': 'application/json',
    'Accept': 'application/json'
  }
})

// Request interceptor — runs before every request
api.interceptors.request.use(
  (config) => {
    const token = localStorage.getItem('token')
    if (token) config.headers.Authorization = `Bearer ${token}`
    return config
  },
  (error) => Promise.reject(error)
)

// Response interceptor — runs after every response
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

4. HTTP Methods

javascript
123456789101112131415161718192021
// src/api/users.js
import api from './axios'

export const userApi = {
  // GET — fetch data
  getAll: () => api.get('/users'),
  getById: (id) => api.get(`/users/${id}`),
  search: (query) => api.get('/users', { params: { q: query, limit: 10 } }),

  // POST — create
  create: (userData) => api.post('/users', userData),

  // PUT — full update
  update: (id, userData) => api.put(`/users/${id}`, userData),

  // PATCH — partial update
  patch: (id, fields) => api.patch(`/users/${id}`, fields),

  // DELETE — remove
  delete: (id) => api.delete(`/users/${id}`)
}

5. Using APIs in Components

vue
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758
<script setup>
import { ref, onMounted } from &#039;vue'
import axios from &#039;axios'

const users = ref([])
const loading = ref(false)
const error = ref(null)
const searchQuery = ref(&#039;')

// Composable-style API call
async function fetchUsers() {
  loading.value = true
  error.value = null
  try {
    const { data } = await axios.get(&#039;https://jsonplaceholder.typicode.com/users')
    users.value = data
  } catch (err) {
    error.value = err.response?.data?.message || err.message || &#039;Failed to load users'
  } finally {
    loading.value = false
  }
}

async function createUser(userData) {
  try {
    const { data } = await axios.post(&#039;https://jsonplaceholder.typicode.com/users', userData)
    users.value.unshift(data)  // Add to beginning of list
    return data
  } catch (err) {
    error.value = &#039;Failed to create user'
    throw err
  }
}

async function deleteUser(id) {
  try {
    await axios.delete(`https://jsonplaceholder.typicode.com/users/${id}`)
    users.value = users.value.filter(u => u.id !== id)
  } catch (err) {
    error.value = &#039;Failed to delete user'
  }
}

onMounted(fetchUsers)
</script>

<template>
  <div>
    <div v-if="loading" class="loading">⏳ Loading...</div>
    <div v-else-if="error" class="error">❌ {{ error }} <button @click="fetchUsers">Retry</button></div>
    <ul v-else>
      <li v-for="user in users" :key="user.id">
        {{ user.name }} — {{ user.email }}
        <button @click="deleteUser(user.id)">Delete</button>
      </li>
    </ul>
  </div>
</template>

6. Mini Project: Weather Application

vue
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100
<!-- WeatherApp.vue -->
<script setup>
import { ref } from &#039;vue'
import axios from &#039;axios'

const city = ref(&#039;London')
const weather = ref(null)
const loading = ref(false)
const error = ref(null)

// Using Open-Meteo (free, no API key needed!)
async function fetchWeather() {
  if (!city.value.trim()) return
  loading.value = true
  error.value = null
  weather.value = null

  try {
    // Step 1: Geocode city to coordinates
    const geoRes = await axios.get(&#039;https://geocoding-api.open-meteo.com/v1/search', {
      params: { name: city.value, count: 1 }
    })
    if (!geoRes.data.results?.length) throw new Error(&#039;City not found')

    const { latitude, longitude, name, country } = geoRes.data.results[0]

    // Step 2: Fetch weather
    const weatherRes = await axios.get(&#039;https://api.open-meteo.com/v1/forecast', {
      params: {
        latitude,
        longitude,
        current: &#039;temperature_2m,wind_speed_10m,weather_code,relative_humidity_2m',
        wind_speed_unit: &#039;kmh'
      }
    })

    weather.value = {
      city: name,
      country,
      ...weatherRes.data.current
    }
  } catch (err) {
    error.value = err.message || &#039;Failed to fetch weather'
  } finally {
    loading.value = false
  }
}

function getWeatherIcon(code) {
  if (code <= 1) return &#039;☀️'
  if (code <= 3) return &#039;⛅'
  if (code <= 67) return &#039;🌧️'
  if (code <= 77) return &#039;❄️'
  return &#039;⛈️'
}
</script>

<template>
  <div class="weather-app">
    <h1>🌍 Weather Forecast</h1>
    <div class="search">
      <input
        v-model="city"
        @keyup.enter="fetchWeather"
        placeholder="Enter city name..."
      />
      <button @click="fetchWeather" :disabled="loading">
        {{ loading ? &#039;⏳' : '🔍 Search' }}
      </button>
    </div>

    <div v-if="loading" class="loading">Fetching weather for {{ city }}...</div>
    <div v-else-if="error" class="error">❌ {{ error }}</div>

    <div v-else-if="weather" class="weather-card">
      <div class="city-name">{{ weather.city }}, {{ weather.country }}</div>
      <div class="weather-icon">{{ getWeatherIcon(weather.weather_code) }}</div>
      <div class="temperature">{{ Math.round(weather.temperature_2m) }}°C</div>
      <div class="details">
        <span>💧 Humidity: {{ weather.relative_humidity_2m }}%</span>
        <span>💨 Wind: {{ Math.round(weather.wind_speed_10m) }} km/h</span>
      </div>
    </div>
  </div>
</template>

<style scoped>
.weather-app { max-width: 420px; margin: 2rem auto; font-family: sans-serif; text-align: center; }
.search { display: flex; gap: .5rem; margin-bottom: 1.5rem; }
.search input { flex: 1; padding: .75rem 1rem; border: 2px solid #e2e8f0; border-radius: 10px; font-size: 1rem; outline: none; }
.search input:focus { border-color: #6366f1; }
.search button { padding: .75rem 1.25rem; background: #6366f1; color: white; border: none; border-radius: 10px; cursor: pointer; font-size: 1rem; font-weight: 600; }
.weather-card { background: linear-gradient(135deg, #1e3a5f, #0ea5e9); color: white; padding: 2.5rem; border-radius: 20px; }
.city-name { font-size: 1.25rem; opacity: .85; margin-bottom: .5rem; }
.weather-icon { font-size: 5rem; margin: .5rem 0; }
.temperature { font-size: 4rem; font-weight: 800; }
.details { display: flex; justify-content: center; gap: 1.5rem; margin-top: 1rem; opacity: .85; }
.loading { color: #64748b; }
.error { color: #ef4444; }
</style>

7. Common Mistakes

  • Not handling loading and error states: Every API call can fail or be slow. Always manage loading, error, and success state separately.
  • Not using .env for API keys: Never hardcode API keys in source code. Use .env.local with VITE_ prefix.

8. MCQs

Question 1

What does the axios { data } destructuring do?

Question 2

Axios interceptors are for?

Question 3

Axios params option in GET request?

Question 4

error.response?.status checks?

Question 5

.env.local API key variable in Vite?

Question 6

What does finally in try/catch/finally guarantee?

Question 7

Axios timeout: 10000 means?

Question 8

POST request with Axios?

Question 9

PATCH vs PUT?

Question 10

Best place to set default Authorization header?

9. Interview Questions

  • Q: What is the difference between axios and fetch for making HTTP requests?
  • Q: How would you implement a global loading indicator for all API calls?

10. Summary

Axios is the definitive HTTP client for Vue — cleaner than fetch, with automatic JSON, interceptors, and better error objects. The loading/error/data state triple is the universal pattern for async data. Centralized Axios instances with interceptors handle auth tokens and global error redirects automatically.

11. Next Chapter Recommendation

In Chapter 19: Working with REST APIs, we build a complete Blog API CRUD application implementing all REST operations.

Finish this Chapter

Save your progress on your learning path and prepare for coding interview challenges.

Discussion

Join the discussion

Log in or create a free account to participate.

Sort: ·