Chapter 12: Framework Integration and Practical Applications

Haiyue
15min

Chapter 12: Framework Integration and Practical Applications

Learning Objectives

  1. Master integrating Axios in Vue.js
  2. Learn to use Axios in React
  3. Understand applications in Node.js
  4. Implement state management integration
  5. Build complete API service layers

12.1 Vue.js Integration

Vue 3 + Composition API

// composables/useApi.js
import { ref, reactive } from 'vue'
import axios from 'axios'

export function useApi(baseURL = '/api') {
  const loading = ref(false)
  const error = ref(null)

  const client = axios.create({
    baseURL,
    timeout: 10000
  })

  // Request interceptor
  client.interceptors.request.use(
    config => {
      loading.value = true
      error.value = null
      return config
    },
    err => {
      loading.value = false
      error.value = err
      return Promise.reject(err)
    }
  )

  // Response interceptor
  client.interceptors.response.use(
    response => {
      loading.value = false
      return response
    },
    err => {
      loading.value = false
      error.value = err
      return Promise.reject(err)
    }
  )

  const get = async (url, config = {}) => {
    try {
      const response = await client.get(url, config)
      return response.data
    } catch (err) {
      throw err
    }
  }

  const post = async (url, data, config = {}) => {
    try {
      const response = await client.post(url, data, config)
      return response.data
    } catch (err) {
      throw err
    }
  }

  return {
    loading: readonly(loading),
    error: readonly(error),
    get,
    post,
    put: (url, data, config) => client.put(url, data, config),
    delete: (url, config) => client.delete(url, config),
    client
  }
}

// Using in component
// UserList.vue
<template>
  <div>
    <div v-if="loading">Loading...</div>
    <div v-else-if="error">Error: {{ error.message }}</div>
    <ul v-else>
      <li v-for="user in users" :key="user.id">
        {{ user.name }} - {{ user.email }}
      </li>
    </ul>
  </div>
</template>

<script setup>
import { ref, onMounted } from 'vue'
import { useApi } from '@/composables/useApi'

const { loading, error, get } = useApi()
const users = ref([])

onMounted(async () => {
  try {
    users.value = await get('/users')
  } catch (err) {
    console.error('Failed to fetch users:', err)
  }
})
</script>

Pinia State Management Integration

// stores/user.js
import { defineStore } from 'pinia'
import { useApi } from '@/composables/useApi'

export const useUserStore = defineStore('user', () => {
  const { get, post, put, delete: del } = useApi()

  const users = ref([])
  const currentUser = ref(null)
  const loading = ref(false)

  const fetchUsers = async () => {
    loading.value = true
    try {
      users.value = await get('/users')
    } catch (error) {
      console.error('Failed to fetch users:', error)
    } finally {
      loading.value = false
    }
  }

  const createUser = async (userData) => {
    try {
      const newUser = await post('/users', userData)
      users.value.push(newUser)
      return newUser
    } catch (error) {
      console.error('Failed to create user:', error)
      throw error
    }
  }

  const updateUser = async (id, userData) => {
    try {
      const updatedUser = await put(`/users/${id}`, userData)
      const index = users.value.findIndex(u => u.id === id)
      if (index > -1) {
        users.value[index] = updatedUser
      }
      return updatedUser
    } catch (error) {
      console.error('Failed to update user:', error)
      throw error
    }
  }

  return {
    users,
    currentUser,
    loading,
    fetchUsers,
    createUser,
    updateUser
  }
})

12.2 React Integration

Custom Hook

// hooks/useApi.js
import { useState, useEffect, useCallback } from 'react'
import axios from 'axios'

export function useApi(baseURL = '/api') {
  const [client] = useState(() => axios.create({
    baseURL,
    timeout: 10000
  }))

  useEffect(() => {
    const requestInterceptor = client.interceptors.request.use(
      config => config,
      error => Promise.reject(error)
    )

    const responseInterceptor = client.interceptors.response.use(
      response => response,
      error => {
        console.error('API error:', error)
        return Promise.reject(error)
      }
    )

    return () => {
      client.interceptors.request.eject(requestInterceptor)
      client.interceptors.response.eject(responseInterceptor)
    }
  }, [client])

  return client
}

// hooks/useUsers.js
import { useState, useEffect } from 'react'
import { useApi } from './useApi'

export function useUsers() {
  const api = useApi()
  const [users, setUsers] = useState([])
  const [loading, setLoading] = useState(false)
  const [error, setError] = useState(null)

  const fetchUsers = useCallback(async () => {
    setLoading(true)
    setError(null)
    try {
      const response = await api.get('/users')
      setUsers(response.data)
    } catch (err) {
      setError(err)
    } finally {
      setLoading(false)
    }
  }, [api])

  const createUser = useCallback(async (userData) => {
    try {
      const response = await api.post('/users', userData)
      setUsers(prev => [...prev, response.data])
      return response.data
    } catch (err) {
      setError(err)
      throw err
    }
  }, [api])

  useEffect(() => {
    fetchUsers()
  }, [fetchUsers])

  return {
    users,
    loading,
    error,
    refetch: fetchUsers,
    createUser
  }
}

React Context Integration

// context/ApiContext.js
import React, { createContext, useContext, useEffect } from 'react'
import axios from 'axios'

const ApiContext = createContext()

export function ApiProvider({ children, baseURL = '/api' }) {
  const client = axios.create({
    baseURL,
    timeout: 10000
  })

  // Setup interceptors
  useEffect(() => {
    const token = localStorage.getItem('token')
    if (token) {
      client.defaults.headers.common['Authorization'] = `Bearer ${token}`
    }

    const responseInterceptor = client.interceptors.response.use(
      response => response,
      error => {
        if (error.response?.status === 401) {
          localStorage.removeItem('token')
          window.location.href = '/login'
        }
        return Promise.reject(error)
      }
    )

    return () => {
      client.interceptors.response.eject(responseInterceptor)
    }
  }, [client])

  return (
    <ApiContext.Provider value={client}>
      {children}
    </ApiContext.Provider>
  )
}

export function useApiClient() {
  const client = useContext(ApiContext)
  if (!client) {
    throw new Error('useApiClient must be used within ApiProvider')
  }
  return client
}

12.3 Node.js Backend Integration

Express Middleware

// middleware/apiClient.js
const axios = require('axios')

function createApiClient(baseURL, timeout = 10000) {
  const client = axios.create({
    baseURL,
    timeout,
    headers: {
      'User-Agent': 'NodeJS-Server/1.0'
    }
  })

  // Request interceptor
  client.interceptors.request.use(
    config => {
      console.log(`Sending request: ${config.method?.toUpperCase()} ${config.url}`)
      return config
    },
    error => {
      console.error('Request configuration error:', error)
      return Promise.reject(error)
    }
  )

  // Response interceptor
  client.interceptors.response.use(
    response => {
      console.log(`Request successful: ${response.status} ${response.config.url}`)
      return response
    },
    error => {
      console.error(`Request failed: ${error.response?.status} ${error.config?.url}`)
      return Promise.reject(error)
    }
  )

  return client
}

// Create clients for different services
const userServiceClient = createApiClient('http://user-service:3001')
const orderServiceClient = createApiClient('http://order-service:3002')

module.exports = {
  userServiceClient,
  orderServiceClient
}

Service Proxy Layer

// services/UserService.js
const { userServiceClient } = require('../middleware/apiClient')

class UserService {
  async getUsers(page = 1, limit = 10) {
    try {
      const response = await userServiceClient.get('/users', {
        params: { page, limit }
      })
      return response.data
    } catch (error) {
      throw new Error(`Failed to get users: ${error.message}`)
    }
  }

  async getUserById(id) {
    try {
      const response = await userServiceClient.get(`/users/${id}`)
      return response.data
    } catch (error) {
      if (error.response?.status === 404) {
        return null
      }
      throw new Error(`Failed to get user: ${error.message}`)
    }
  }

  async createUser(userData) {
    try {
      const response = await userServiceClient.post('/users', userData)
      return response.data
    } catch (error) {
      if (error.response?.status === 400) {
        throw new Error(`Failed to create user: ${error.response.data.message}`)
      }
      throw new Error(`Failed to create user: ${error.message}`)
    }
  }
}

module.exports = new UserService()

12.4 Complete Application Example

Frontend-Backend Separation Architecture

// Frontend - API Service Layer
// services/ApiService.js
class ApiService {
  constructor() {
    this.client = axios.create({
      baseURL: process.env.VUE_APP_API_BASE_URL || '/api',
      timeout: 10000
    })

    this.setupInterceptors()
  }

  setupInterceptors() {
    // Request interceptor - add authentication
    this.client.interceptors.request.use(config => {
      const token = this.$store.getters['auth/token']
      if (token) {
        config.headers.Authorization = `Bearer ${token}`
      }
      return config
    })

    // Response interceptor - handle errors
    this.client.interceptors.response.use(
      response => response,
      error => {
        if (error.response?.status === 401) {
          this.$store.dispatch('auth/logout')
          this.$router.push('/login')
        }

        return Promise.reject(error)
      }
    )
  }

  // User-related APIs
  users = {
    list: (params) => this.client.get('/users', { params }),
    get: (id) => this.client.get(`/users/${id}`),
    create: (data) => this.client.post('/users', data),
    update: (id, data) => this.client.put(`/users/${id}`, data),
    delete: (id) => this.client.delete(`/users/${id}`)
  }

  // Order-related APIs
  orders = {
    list: (params) => this.client.get('/orders', { params }),
    get: (id) => this.client.get(`/orders/${id}`),
    create: (data) => this.client.post('/orders', data),
    update: (id, data) => this.client.put(`/orders/${id}`, data),
    cancel: (id) => this.client.post(`/orders/${id}/cancel`)
  }
}

export default new ApiService()

Error Boundaries and Recovery

// utils/errorHandler.js
export class ApiErrorHandler {
  static handle(error, context = {}) {
    const errorInfo = {
      url: error.config?.url,
      method: error.config?.method,
      status: error.response?.status,
      message: error.message,
      context
    }

    // Log error
    console.error('API error:', errorInfo)

    // Handle differently based on error type
    if (error.response) {
      return this.handleResponseError(error.response, context)
    } else if (error.request) {
      return this.handleNetworkError(error, context)
    } else {
      return this.handleConfigError(error, context)
    }
  }

  static handleResponseError(response, context) {
    const { status, data } = response

    switch (status) {
      case 400:
        return { message: 'Invalid request parameters', canRetry: false }
      case 401:
        return { message: 'Please login again', canRetry: false, requiresAuth: true }
      case 403:
        return { message: 'No permission to access', canRetry: false }
      case 404:
        return { message: 'Requested resource not found', canRetry: false }
      case 500:
        return { message: 'Server error, please try again later', canRetry: true }
      default:
        return { message: data?.message || 'Request failed', canRetry: true }
    }
  }

  static handleNetworkError(error, context) {
    if (error.code === 'ECONNABORTED') {
      return { message: 'Request timeout, please try again later', canRetry: true }
    }
    return { message: 'Network connection failed, please check your network', canRetry: true }
  }
}

Chapter Summary

Through this chapter, we have mastered:

  • Using Axios with Composition API and Pinia in Vue.js
  • Using Axios through custom Hooks and Context in React
  • Axios applications in Node.js backend environment
  • Building complete API service layer architecture
  • Unified error handling and recovery mechanisms

Complete Axios Course Summary

After 12 chapters of systematic learning, we started from Axios basics and gradually progressed to advanced applications, finally completing comprehensive Axios HTTP client learning:

Core Skills Mastered

  • Basic Skills: HTTP methods, request configuration, response handling, error handling
  • Advanced Skills: Interceptor mechanisms, instance configuration, request cancellation, concurrency control
  • Advanced Applications: File upload/download, authentication, performance optimization, TypeScript integration
  • Practical Applications: Framework integration, enterprise architecture, best practices

Key Features Understanding

  • Interceptors provide powerful request/response processing capabilities
  • Instantiation supports multi-service and multi-environment configuration
  • Comprehensive error handling mechanisms ensure application stability
  • TypeScript integration provides type safety guarantees
  • Framework integration adapts to different development environments

Production-Level Practices

  • Secure authentication mechanisms and token management
  • Efficient caching and performance optimization strategies
  • Comprehensive error recovery and user experience
  • Maintainable code architecture and best practices
  • Comprehensive testing and monitoring solutions

Axios, as the most important HTTP client library in modern web development, provides us with complete network communication solutions. Mastering this knowledge and skills will provide a solid foundation for building high-quality web applications.

Key Points

  • Different frameworks have their own integration best practices
  • State management integration simplifies data flow management
  • Unified API service layer improves code reusability
  • Comprehensive error handling mechanisms enhance user experience
  • Continuously learn new API design patterns and best practices