Capítulo 15: Consumo de APIs REST com Axios / ofetch e Interceptors

Especialização em Front-End com Vue • Vue 3 & Nuxt 3 • Composition API, Pinia, Vue Router, SSR e TypeScript


🗺️ Mapa Conceitual da Camada de Integração HTTP

flowchart TD
    A["Componente Vue / Composable"] --> B["Instância Axios / ofetch Customizada"]
    B --> C["1. Request Interceptor: Injeta Bearer Token & Correlation ID"]
    C --> D["Rede Externa / Gateway de Microsserviços"]

    D --> E{"Status HTTP da Resposta"}
    E -->|200 OK / 201 Created| F["2. Response Interceptor: Retorna response.data"]
    E -->|"401 Unauthorized (Expirado)"| G["3. Fila de Concorrência de Refresh Token"]
    E -->|403 / 500 / Erro de Rede| H["Tratamento Centralizado de Exceções"]

    G -->|Renovação Bem-Sucedida| I["Reexecuta Requisições Originais com Novo Token"]
    G -->|Falha no Refresh| J["Limpa Sessão e Expulsa para /login"]
    F & I --> K["Retorno Reativo para o Componente (Refs)"]

    style A fill:#e1f5fe,stroke:#03a9f4,stroke-width:2px
    style B fill:#fff3e0,stroke:#ff9800,stroke-width:2px
    style C fill:#ede7f6,stroke:#7e57c2,stroke-width:2px
    style E fill:#fff9c4,stroke:#fbc02d,stroke-width:2px
    style K fill:#e8f5e9,stroke:#4caf50,stroke-width:2px

🏛️ 1. Arquitetura da Camada de Serviços HTTP Corporativa

Em aplicações Vue corporativas, componentes visuais nunca devem realizar chamadas de rede diretas utilizando URLs soltas. A comunicação externa deve ser desacoplada em uma Camada de Cliente HTTP Centralizada, dotada de interceptores de segurança, tratamento de concorrência e suporte a cancelamento de requisições.

1.1 Axios vs Fetch Nativo vs ofetch (Nuxt)

  • Axios: A biblioteca mais madura para SPAs puras com Vite. Oferece interceptores simétricos de request e response, serialização automática de JSON e detecção consistente de timeouts de rede.
  • ofetch (Nativo do Nuxt 3): Wrapper sobre o fetch moderno que suporta serialização automática, execução universal perfeita (Node + Browser) e retentativas automáticas inteligentes.

1.2 O Interceptor de Requisição (Autenticação Bearer)

Antes de qualquer pacote sair do navegador, o interceptor de requisição lê o token de acesso (JWT) armazenado na memória ou cookie seguro e injeta o cabeçalho padrão:

Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
X-Correlation-ID: 7a8f9c10-e421-4f32-841a-982312b910ab

1.3 O Desafio do HTTP 401 e a Fila de Concorrência de Refresh

Quando um token JWT de curta duração expira, requisições paralelas podem falhar com HTTP 401 simultaneamente. Se a aplicação disparar 5 chamadas de /auth/refresh ao mesmo tempo, a chave de rotação de refresh token do servidor será invalidada (Reuse Detection).

  • A Solução: Um mecanismo de bloqueio com fila de promessas (Pending Queue). A primeira requisição com 401 trava as demais, realiza a renovação de forma atômica e, assim que o novo token é emitido, desempilha e reexecuta todas as requisições originais de forma totalmente invisível para o usuário final.

1.4 Cancelamento Ativo com AbortController

Se o usuário digitar caracteres em um campo de busca rápida ou mudar de rota antes de um download pesado terminar, a requisição anterior deve ser cancelada imediatamente via AbortController.signal. Isso economiza largura de banda e impede que respostas antigas sobrescrevam o estado atual (Race Conditions).


⚖️ 2. Tabela Comparativa de Clientes HTTP

Recurso Técnico fetch() Nativo axios ofetch (Nuxt 3)
Interceptores Globais Manual (exige wrapper próprio). Nativos e Poderosos (use). Suporta ganchos (onRequest, onResponse).
Serialização de JSON Manual (response.json()). Automática. Automática.
Timeouts Nativos Manual com AbortSignal.timeout. Configurável (timeout: 10000). Configurável (timeout: 10000).
Suporte Universal SSR Exige polyfill no Node antigo. Isomórfico. Projetado do zero para SSR.

⚠️ 3. Armadilhas Comuns em Produção (Gotchas & Anti-Patterns)

Anti-Pattern 1: Loop Infinito no Response Interceptor

Erro Comum: Se a própria chamada do endpoint /auth/refresh retornar HTTP 401 e o interceptor não verificar uma flag _retry, ele tentará renovar o token infinitamente, travando a thread do navegador. Abordagem Correta: Adicione uma flag de controle config._retry = true e descarte a tentativa se a rota de renovação falhar.

Anti-Pattern 2: Não Cancelar Consultas no Desmonte do Componente

Erro Comum: O usuário entra em uma tela pesada, a API demora 3 segundos, ele volta para a home. Quando a Promise finalmente resolve, ela tenta mutar o estado de um componente que já foi desmontado. Abordagem Correta: Conecte um AbortController ao hook onScopeDispose ou onUnmounted.


💻 4. Código Conceitual Arquitetural Comentado

// src/services/httpService.ts
import axios, { type AxiosInstance, type InternalAxiosRequestConfig } from 'axios';

export const http: AxiosInstance = axios.create({
  baseURL: import.meta.env.VITE_API_URL || 'https://api.empresa.com.br/v1',
  timeout: 15000,
  headers: { 'Content-Type': 'application/json' }
});

// Interceptor de Injeção de Segurança
http.interceptors.request.use((config: InternalAxiosRequestConfig) => {
  const token = localStorage.getItem('jwt_access_token');
  if (token && config.headers) {
    config.headers.Authorization = `Bearer ${token}`;
  }
  return config;
});

// Interceptor de Resiliência de Erros
http.interceptors.response.use(
  (response) => response,
  (error) => {
    if (error.response?.status === 403) {
      console.warn('[HTTP 403] Acesso negado. Usuário sem permissões suficientes.');
    }
    return Promise.reject(error);
  }
);

🛠️ 5. Cheat-Sheet: Cancelamento com AbortController

// Criando requisição cancelável no Vue 3
import { onScopeDispose } from 'vue';

const controller = new AbortController();

http.get('/relatorio-pesado', { signal: controller.signal })
  .then(res => { ... })
  .catch(err => {
    if (axios.isCancel(err)) {
      console.log('Requisição abortada pelo usuário.');
    }
  });

// Limpeza garantida ao sair da tela
onScopeDispose(() => {
  controller.abort();
});