Skip to content

Kapitel 37: axios (Teil 2) - Instanz & Interceptors

📙 Lernziel: axios Instanz erstellen & Interceptors meistern!


37.1 Was ist eine axios Instanz?

axios Instanz erlaubt wiederverwendbare Konfigurationen.

Vorteile:

  • ✅ Basis-URL festlegen (baseURL)
  • ✅ Standard-Header setzen
  • ✅ Timeout konfigurieren
  • ✅ Interceptors hinzufügen

Beispiel:

javascript
// utils/axiosInstance.js
import axios from 'axios'

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

export default apiClient

37.2 axios Instanz verwenden

Beispiel:

vue
<!-- views/PostsView.vue -->
<script setup>
import { ref, onMounted } from 'vue'
import apiClient from '../utils/axiosInstance.js'

const posts = ref([])
const isLoading = ref(false)
const error = ref(null)

const fetchPosts = async () => {
  isLoading.value = true
  error.value = null
  
  try {
    const response = await apiClient.get('/posts?_limit=5')
    posts.value = response.data
  } catch (err) {
    error.value = err.message
  } finally {
    isLoading.value = false
  }
}

onMounted(fetchPosts)
</script>

<template>
  <div class="posts-view">
    <h2>Posts</h2>
    
    <div v-if="isLoading">Lädt...</div>
    <div v-else-if="error" class="error">{{ error }}</div>
    <ul v-else>
      <li v-for="post in posts" :key="post.id">
        <h3>{{ post.title }}</h3>
        <p>{{ post.body }}</p>
      </li>
    </ul>
    
    <button @click="fetchPosts()">Neu laden</button>
  </div>
</template>

<style scoped>
.posts-view {
  max-width: 800px;
  margin: 0 auto;
  padding: 20px;
}

.error {
  color: red;
  padding: 10px;
  background: #ffebee;
  border-radius: 4px;
}

button {
  padding: 10px 20px;
  background: #42b883;
  color: white;
  border: none;
  border-radius: 5px;
  cursor: pointer;
  margin-top: 20px;
}
</style>

37.3 Request Interceptor

Request Interceptor wird vor jedem Request ausgeführt.

Anwendungsfälle:

  • ✅ Token hinzufügen (Authorization Header)
  • ✅ Loading-State aktivieren
  • ✅ Request-Logging

Beispiel:

javascript
// utils/axiosInstance.js
import axios from 'axios'

const apiClient = axios.create({
  baseURL: 'https://jsonplaceholder.typicode.com'
})

// Request Interceptor
apiClient.interceptors.request.use(
  (config) => {
    // Token aus localStorage hinzufügen
    const token = localStorage.getItem('token')
    if (token) {
      config.headers.Authorization = `Bearer ${token}`
    }
    
    console.log('Request:', config.method.toUpperCase(), config.url)
    return config
  },
  (error) => {
    return Promise.reject(error)
  }
)

export default apiClient

37.4 Response Interceptor

Response Interceptor wird nach jedem Response ausgeführt.

Anwendungsfälle:

  • ✅ Token-Refresh bei 401 Fehler
  • ✅ Globales Error-Handling
  • ✅ Response-Transformation

Beispiel:

javascript
// utils/axiosInstance.js
import axios from 'axios'
import { useRouter } from 'vue-router'

const apiClient = axios.create({
  baseURL: 'https://jsonplaceholder.typicode.com'
})

// Response Interceptor
apiClient.interceptors.response.use(
  (response) => {
    console.log('Response:', response.status, response.config.url)
    return response
  },
  async (error) => {
    const router = useRouter()
    
    if (error.response) {
      // Server antwortet mit Fehler-Status
      if (error.response.status === 401) {
        // Token abgelaufen → Redirect zu Login
        localStorage.removeItem('token')
        router.push('/login')
      } else if (error.response.status === 403) {
        // Keine Berechtigung
        alert('Keine Berechtigung!')
      } else if (error.response.status === 500) {
        // Server-Fehler
        alert('Server-Fehler! Bitte später erneut versuchen.')
      }
    } else if (error.request) {
      // Keine Antwort vom Server
      alert('Keine Verbindung zum Server!')
    } else {
      // Fehler beim Request-Setup
      console.error('Request Fehler:', error.message)
    }
    
    return Promise.reject(error)
  }
)

export default apiClient

37.5 Vollständiges Beispiel (axios Instanz + Interceptors)

utils/apiClient.js:

javascript
// utils/apiClient.js
import axios from 'axios'
import { useRouter } from 'vue-router'

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

// Request Interceptor
apiClient.interceptors.request.use(
  (config) => {
    // Token hinzufügen
    const token = localStorage.getItem('token')
    if (token) {
      config.headers.Authorization = `Bearer ${token}`
    }
    
    // Loading-State (optional)
    // store.dispatch('setLoading', true)
    
    return config
  },
  (error) => {
    return Promise.reject(error)
  }
)

// Response Interceptor
apiClient.interceptors.response.use(
  (response) => {
    // Loading-State beenden (optional)
    // store.dispatch('setLoading', false)
    
    return response
  },
  async (error) => {
    const router = useRouter()
    
    if (error.response) {
      const { status } = error.response
      
      if (status === 401) {
        // Token abgelaufen → Refresh Token versuchen
        const refreshToken = localStorage.getItem('refreshToken')
        
        if (refreshToken) {
          try {
            const response = await axios.post('https://api.example.com/auth/refresh', {
              refreshToken
            })
            
            const { token } = response.data
            localStorage.setItem('token', token)
            
            // Original Request mit neuem Token wiederholen
            error.config.headers.Authorization = `Bearer ${token}`
            return apiClient(error.config)
          } catch (refreshError) {
            // Refresh Token auch abgelaufen → Logout
            localStorage.removeItem('token')
            localStorage.removeItem('refreshToken')
            router.push('/login')
          }
        } else {
          router.push('/login')
        }
      } else if (status === 403) {
        alert('Keine Berechtigung!')
      } else if (status === 500) {
        alert('Server-Fehler!')
      }
    } else if (error.request) {
      alert('Keine Verbindung zum Server!')
    }
    
    return Promise.reject(error)
  }
)

export default apiClient

Verwendung:

vue
<!-- views/ProfileView.vue -->
<script setup>
import { ref, onMounted } from 'vue'
import apiClient from '../utils/apiClient.js'

const user = ref(null)
const isLoading = ref(false)

const fetchUserProfile = async () => {
  isLoading.value = true
  
  try {
    const response = await apiClient.get('/user/profile')
    user.value = response.data
  } catch (error) {
    console.error('Fehler:', error)
  } finally {
    isLoading.value = false
  }
}

onMounted(fetchUserProfile)
</script>

<template>
  <div class="profile-view">
    <h2>Profil</h2>
    
    <div v-if="isLoading">Lädt...</div>
    <div v-else-if="user">
      <h3>{{ user.name }}</h3>
      <p>Email: {{ user.email }}</p>
      <p>Stadt: {{ user.city }}</p>
    </div>
    <div v-else>
      <p>Keine Daten gefunden.</p>
    </div>
  </div>
</template>

37.6 Übung: API Client mit Interceptors

Aufgabe: Erstelle einen API Client mit Request & Response Interceptors.

Lösung:

1. API Client erstellen (utils/api.js):

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

const api = axios.create({
  baseURL: 'https://jsonplaceholder.typicode.com',
  timeout: 5000
})

// Request Interceptor (Token hinzufügen)
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 (Fehlerbehandlung)
api.interceptors.response.use(
  (response) => response,
  (error) => {
    if (error.response && error.response.status === 401) {
      localStorage.removeItem('token')
      window.location.href = '/login'
    }
    return Promise.reject(error)
  }
)

export default api

2. Verwendung in Komponente:

vue
<!-- views/TodosView.vue -->
<script setup>
import { ref, onMounted } from 'vue'
import api from '../utils/api.js'

const todos = ref([])
const isLoading = ref(false)

const fetchTodos = async () => {
  isLoading.value = true
  
  try {
    const response = await api.get('/todos?_limit=5')
    todos.value = response.data
  } catch (error) {
    console.error('Fehler:', error)
  } finally {
    isLoading.value = false
  }
}

onMounted(fetchTodos)
</script>

<template>
  <div>
    <h2>Todos</h2>
    <div v-if="isLoading">Lädt...</div>
    <ul v-else>
      <li v-for="todo in todos" :key="todo.id">
        <input type="checkbox" :checked="todo.completed" />
        <span :style="{ textDecoration: todo.completed ? 'line-through' : 'none' }">
          {{ todo.title }}
        </span>
      </li>
    </ul>
  </div>
</template>

✅ Zusammenfassung

In diesem Kapitel hast du gelernt:

  • ✅ Was eine axios Instanz ist
  • ✅ axios Instanz erstellen & verwenden
  • ✅ Request Interceptor (Token hinzufügen)
  • ✅ Response Interceptor (Fehlerbehandlung)
  • ✅ Vollständiges Beispiel (Token Refresh)
  • ✅ Praxis: API Client mit Interceptors

🎯 Nächster Schritt: In Kapitel 38 lernst du CORS (Cross-Origin Resource Sharing)!


← Zurück zu Kapitel 36: axios GrundlagenWeiter zu Kapitel 38: CORS →

Frei für alle Anfänger