Capítulo 15: Consumo de APIs REST com Axios / ofetch e Interceptors
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 ofetchmoderno 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();
});