📚 Módulo 07: Frontend - Contexto de Login, Interceptor Axios e Layout Glassmorphic
Neste módulo, conectaremos a segurança de ponta a ponta na nossa aplicação. Desenvolveremos o Contexto de Autenticação Reativa (AuthContext) para manter o estado de login do usuário de forma global e persistente no React, programaremos um Interceptador do Axios para injetar automaticamente as credenciais JWT nas chamadas à API e criaremos um Design System Glassmorphic responsivo e elegante em CSS Puro.
⚡ Fluxo do Interceptador Axios
O interceptador age como um middleware no lado do cliente. Toda vez que você fizer api.get('/produtos'), ele pausa a requisição, anexa o cabeçalho seguro e libera a continuação.
sequenceDiagram
participant Componente as React Component
participant Axios as Interceptador Axios
participant API as NestJS API
Componente->>Axios: POST /pedidos/checkout (Sem header)
Axios->>Axios: Lê localStorage ('tecloja_user')
alt Possui Token
Axios->>Axios: Anexa Authorization: Bearer <TOKEN>
end
Axios->>API: Dispara requisição HTTP
API-->>Axios: Resposta 200 OK
Axios-->>Componente: Retorna JSON
🔐 1. O Contexto de Autenticação Reativa (AuthContext)
No React, para compartilhar dados entre múltiplos componentes sem precisar passar propriedades manualmente de pai para filho (Prop Drilling), utilizamos a Context API.
Criaremos o contexto que gerencia a gravação segura e remoção do token JWT armazenado no localStorage. Crie o arquivo em src/contexts/AuthContext.tsx:
// src/contexts/AuthContext.tsx
import React, { createContext, useState, useEffect, useContext } from 'react';
import { UsuarioLogado } from '../types';
interface AuthContextType {
usuario: UsuarioLogado | null;
logado: boolean;
isAdmin: boolean;
login: (dados: UsuarioLogado) => void;
logout: () => void;
}
const AuthContext = createContext<AuthContextType>(null!);
export const AuthProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
const [usuario, setUsuario] = useState<UsuarioLogado | null>(null);
// Carregar token gravado no início da aplicação (Persistência)
useEffect(() => {
const usuarioSalvo = localStorage.getItem('tecloja_user');
if (usuarioSalvo) {
setUsuario(JSON.parse(usuarioSalvo));
}
}, []);
const login = (dados: UsuarioLogado) => {
localStorage.setItem('tecloja_user', JSON.stringify(dados));
setUsuario(dados);
};
const logout = () => {
localStorage.removeItem('tecloja_user');
setUsuario(null);
};
const logado = !!usuario;
const isAdmin = usuario?.role === 'ADMIN';
return (
<AuthContext.Provider value={{ usuario, logado, isAdmin, login, logout }}>
{children}
</AuthContext.Provider>
);
};
// Hook customizado para facilitar o acesso às informações de segurança
export const useAuth = () => useContext(AuthContext);
⚡ 2. Injeção Criptográfica Automatizada (Axios Request Interceptor)
Para que o desenvolvedor não precise incluir manualmente o cabeçalho HTTP de autorização (Authorization: Bearer <TOKEN>) em cada método de consulta ou salvamento da API, configuraremos um Interceptador do Axios.
Ele inspecionará todas as requisições enviadas ao servidor. Se houver um token ativo no localStorage, ele injetará o cabeçalho Bearer de forma transparente.
Crie o arquivo em src/services/api.ts:
// src/services/api.ts
import axios from 'axios';
const api = axios.create({
baseURL: import.meta.env.VITE_API_URL || 'http://localhost:3000',
timeout: 10000,
});
// Interceptor de Requisição do Axios
api.interceptors.request.use(
(config) => {
const usuarioSalvo = localStorage.getItem('tecloja_user');
if (usuarioSalvo) {
const { token } = JSON.parse(usuarioSalvo);
if (token && config.headers) {
// Injeta automaticamente o token JWT
config.headers.Authorization = `Bearer ${token}`;
}
}
return config;
},
(error) => {
return Promise.reject(error);
}
);
// Interceptor de Resposta (captura erros de Token expirado / 401 Unauthorized)
api.interceptors.response.use(
(response) => response,
(error) => {
if (error.response && error.response.status === 401) {
console.warn('⚠️ Token expirado ou não autorizado. Redirecionando para logout.');
localStorage.removeItem('tecloja_user');
window.location.href = '/login';
}
return Promise.reject(error);
}
);
export default api;
🎨 3. O Design System Responsivo Glassmorphic em CSS Puro
Para proporcionar uma experiência visual premium de tirar o fôlego baseada nas tendências modernas de design de interfaces web, usaremos efeitos estéticos de transparência com desfoque de fundo (glassmorphism), gradientes harmônicos e micro-animações.
Crie o arquivo de estilos em src/assets/styles.css:
/* src/assets/styles.css */
@import url('https://fonts.googleapis.com/css2?family=Outfit:wght@300;400;500;600;700&display=swap');
:root {
--font-principal: 'Outfit', sans-serif;
--bg-gradient: linear-gradient(135deg, #0b0f19 0%, #111827 100%);
--glass-bg: rgba(255, 255, 255, 0.03);
--glass-border: rgba(255, 255, 255, 0.07);
--glass-shadow: rgba(0, 0, 0, 0.3);
--glass-blur: blur(12px);
--primaria: #61dafb;
--primaria-hover: #4fa8c7;
--texto: #f3f4f6;
--texto-secundario: #9ca3af;
--sucesso: #10b981;
--perigo: #ef4444;
}
body {
margin: 0;
font-family: var(--font-principal);
background: var(--bg-gradient);
color: var(--texto);
min-height: 100vh;
overflow-x: hidden;
}
/* Efeito Premium de Vidro (Glassmorphism) */
.card-glass {
background: var(--glass-bg);
backdrop-filter: var(--glass-blur);
-webkit-backdrop-filter: var(--glass-blur);
border: 1px solid var(--glass-border);
box-shadow: 0 8px 32px 0 var(--glass-shadow);
border-radius: 16px;
padding: 24px;
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}
.card-glass:hover {
border-color: rgba(97, 218, 251, 0.3);
transform: translateY(-4px);
box-shadow: 0 12px 40px 0 rgba(97, 218, 251, 0.1);
}
/* Estilização dos Formulários Reativos */
.form-grupo {
margin-bottom: 20px;
display: flex;
flex-direction: column;
gap: 8px;
}
.form-campo {
background: rgba(0, 0, 0, 0.3);
border: 1px solid var(--glass-border);
border-radius: 8px;
padding: 12px 16px;
color: var(--texto);
font-family: var(--font-principal);
font-size: 1rem;
transition: all 0.3s ease;
}
.form-campo:focus {
outline: none;
border-color: var(--primaria);
box-shadow: 0 0 0 2px rgba(97, 218, 251, 0.2);
}
/* Botões Reativos Premium */
.btn-premium {
font-family: var(--font-principal);
font-weight: 600;
font-size: 1rem;
padding: 12px 24px;
border-radius: 8px;
border: none;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
gap: 8px;
transition: all 0.3s ease;
}
.btn-primario {
background: linear-gradient(135deg, #61dafb 0%, #3b82f6 100%);
color: #0b0f19;
}
.btn-primario:hover {
transform: scale(1.02);
box-shadow: 0 0 20px rgba(97, 218, 251, 0.4);
}
.btn-perigo {
background: var(--perigo);
color: #fff;
}
.btn-perigo:hover {
background: #dc2626;
box-shadow: 0 0 15px rgba(239, 68, 68, 0.4);
}
✅ Pré-Requisitos deste Módulo
Antes de passar para a criação dos hooks do carrinho de compras e formulários CRUD, certifique-se de que:
- A SPA React foi gerada via Vite e possui o pacote
react-router-domconfigurado no Módulo 06. - A dependência
axiosestá devidamente instalada na pasta/tecloja-frontend. - O arquivo CSS global de layout e design system foi criado e importado no arquivo
src/main.tsx.
🤔 Por que fizemos assim?
- Por que usar a Context API (
AuthContext) para gerenciar a autenticação em vez de prop drilling? A sessão de login (usuário e token JWT) é um estado transversal exigido em quase todas as partes da tela (como na Navbar para exibir o nome do usuário, nos botões de compra para habilitar ações, e nos Guards de proteção). Passar essas propriedades manualmente por dezenas de componentes intermediários (Prop Drilling) torna o código complexo e difícil de manter. O contexto do React atua como um barramento global de dados, permitindo que qualquer componente-filho consulte ou modifique a sessão de forma direta e limpa. - Por que programar interceptadores de Requisição E de Resposta no Axios? O interceptador de requisição centraliza a segurança, anexando automaticamente a diretiva
Authorization: Bearer <token>em qualquer requisição que passe pela nossa instânciaapi. O interceptador de resposta completa o fluxo: caso a credencial do usuário expire em tempo de uso, a API NestJS retornará o código401 Unauthorized. O interceptador captura essa falha de forma global, limpa olocalStoragedo navegador e redireciona o usuário para a view de/login, evitando anomalias na interface React. - Por que carregar o token no
useEffectde inicialização do provedor do Contexto? Por ser uma aplicação do lado do cliente (SPA), qualquer atualização de página (F5) limpa o estado reativo (useState) da aplicação na memória. Ler as informações salvas nolocalStoragedurante a montagem inicial do<AuthProvider>restabelece a sessão do usuário de forma automática e transparente, preservando a experiência de navegação (UX) sem exigir novos logins.
🔍 Checkpoint
- Acoplamento do Provider: Garanta que o componente
<AuthProvider>foi declarado emsrc/main.tsxenvolvendo o componente raiz<App />. - Injeção de Cabeçalho: Logue na aplicação e verifique no console de rede do navegador (Aba Network) se as chamadas de API feitas pela instância
apicontêm o cabeçalhoAuthorization: Bearer .... - Redirecionamento Automático: Exclua manualmente o token do
localStoragevia aba Application do navegador durante o uso e tente acessar um endpoint privado. Valide se a aplicação redireciona você instantaneamente para a tela/login.
⚠️ Erros Comuns
| Erro | Causa | Solução |
|---|---|---|
TypeError: Cannot read properties of undefined (reading 'usuario') ao chamar useAuth |
O componente que consome o hook useAuth() está renderizado fora do escopo físico do provedor <AuthProvider>. |
Certifique-se de que o <AuthProvider> foi registrado no topo da hierarquia de componentes da aplicação, idealmente envolvendo o componente <App /> em src/main.tsx. |
| O token JWT não é enviado nas requisições HTTP do frontend | O desenvolvedor realizou a importação do axios padrão (import axios from 'axios') em vez da nossa instância configurada. |
Garanta o uso exclusivo da instância do axios customizada para conexões privadas: import api from '../services/api'. |
Loop infinito de redirecionamento para /login no navegador |
O interceptador de resposta redireciona o usuário para /login e a própria rota tenta disparar chamadas REST que resultam em novas falhas 401. |
Certifique-se de que rotas e consultas públicas (como login e consulta inicial do catálogo) não disparem interceptadores de falhas críticas ou limpe as credenciais antes de realizar a navegação. |
🏁 Conclusão
Com o contexto de autenticação global estruturado, interceptadores criptográficos anexando tokens JWT automaticamente e o sistema de design responsivo glassmorphic definido, nossa infraestrutura de frontend está pronta!
No Módulo 08, focaremos no desenvolvimento das interações do usuário. Programaremos o Carrinho de Compras Reativo usando Custom Hooks para gerenciar compras e recalcular totais de forma instantânea, e criaremos os formulários administrativos de CRUD de Produtos completos com feedback e validação instantânea visual de campos.