O Axios é sem sombra de dúvida o cliente HTTP mais popular do mundo JavaScript e arrisco dizer que é mais popular até do que a API nativa fetch. Extremamente prático e eficiente, você consegue com poucas linhas de código implementar aquela chamada de API REST que seu sistema precisa e com um tamanho muito pequeno de carga na aplicação.
Se você não sabe nada sobre o Axios e quer aprender o básico, o vídeo abaixo pode ajudar.
Como todos HTTP clients, você pode usá-lo no seu backend ou no seu frontend, conforme a necessidade, mas é interessante que tenha um conhecimento de HTTP primeiro. No entanto, tão popular quanto usar o Axios é repetir código usando ele. Não raro os códigos dos desenvolvedores JavaScript se tornam cheios de lógicas repetidas ou ao menos chamadas de funções repetidas para fazer atividades como definir cabeçalhos, carregar tokens, transformar respostas e por aí vai. E não tem nada de errado em criar funções para estas atividades repetitivas, exceto pelo fato de que o próprio Axios possui um mecanismo para lidar com essas transformações e configurações de maneira inteligente e na caixa.
Esse recurso se chama Interceptors e o tutorial de hoje é justamente sobre isso. Se preferir, você pode aprender sobre interceptors no vídeo abaixo, ao invés de ler este tutorial.
Projeto de Exemplo
Para os exemplos nós precisamos de um pequeno projeto.
Primeiro, vamos criar uma simples web api com duas versões: sem segurança e com segurança. Não darei muitos detalhes, pois fogem do escopo deste tutorial. Também sinta-se à vontade de pular esta etapa caso deseje apenas ver os exemplos de interceptors.
Crie uma pasta webapi na sua máquina e dentro rode os comandos abaixo.
1 2 3 4 |
npm init -y npm install cors dotenv express |
Agora vamos criar nossa API com “segurança”, exigindo sempre um token “123” no cabeçalho Authorization para funcionar. Este é o conteúdo do api-segura.js:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
//api-segura.js const http = require('http'); const express = require('express'); const app = express(); app.use(require("cors")()); app.use(express.json()); app.use((req, res, next) => { const token = req.headers['authorization']; if (token === "123") { res.locals.token = token; return next(); } res.status(401).json('Unauthorized'); }) app.get('/', (req, res, next) => { res.json({ message: "Tudo ok por aqui!", dados: cadastros }); }) const cadastros = []; app.post('/cadastro', (req, res, next) => { console.log("Cadastro recebido!"); //salva no banco de dados cadastros.push({ nome: req.body.txtNome, idade: parseInt(req.body.txtIdade), uf: req.body.cmbUF }); res.json({ message: "Cadastro recebido!", dados: cadastros }); }) const server = http.createServer(app); server.listen(process.env.PORT || 3001); console.log(`Servidor escutando na porta ${process.env.PORT || 3001}...`) |
Para executar essa API e deixar ela rodando, use o comando abaixo. Deixe ela rodando em um terminal e depois siga em frente, usando outro terminal.
1 2 3 |
node api-segura.js |
Agora vamos criar o projeto de frontend. Faremos ele com React.js, que você pode aprender mais neste tutorial aqui. Mas se não souber nada, não se preocupe, basta seguir as instruções. Vá até outra pasta da sua máquina e rode os comandos abaixo para criar o projeto de frontend usando React.js e configurá-lo.
1 2 3 4 5 |
npx create-react-app react-axios cd react-axios npm install axios |
Agora crie um arquivo .env na raiz do seu projeto de front e coloque o seguinte conteúdo nele. Essa configuração indica para o frontend onde está o seu backend, que a essa altura deve estar executando em outra janela de terminal.
1 2 3 |
REACT_APP_API_URL=http://localhost:3001 |
Agora crie uma pasta services dentro de src. Essa pasta services vai ter dois arquivos. O primeiro é o APIService.js, como abaixo.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
import axios from "axios"; const API_URL = process.env.REACT_APP_API_URL; export async function authRequest() { const body = { nome: 'LuizTools', idade: 35, uf: 'RS' }; const response = await axios({ method: 'POST', url: `${API_URL}/cadastro`, data: body, headers: { 'Authorization': "123" } }) return response.data; } |
Esta é uma função de requisição autenticada. Esse token “123” que ela usa no cabeçalho Authorization geralmente fica salvo no browser usando localStorage ou cookies e deve ser enviado em toda requisição. É aí que mora o problema: o código de obtenção e injeção do token na request acaba se repetindo em diversos pontos da aplicação, o que gera problemas futuros de manutenção e a deixa mais propensa a bugs por esquecimento do mesmo.
Para exemplificar uma chamada, podemos modificar o src/pages/App.js que vem no projeto React padrão para o seguinte. Ajuste o caminho até o módulo APIService.js se sua estrutura de pastas estiver diferente da minha.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
import { authRequest } from '../services/APIService'; export default function App() { function btnAuthRequestClick() { authRequest() .then(data => console.log(data)) .catch(err => console.error(err)); } return ( <main> <div> <button onClick={btnAuthRequestClick}>Auth Request</button> </div> </main> ) } |
Execute a sua aplicação de frontend com o comando abaixo, e deixe ela rodando.
1 2 3 |
npm start |
Agora vamos estudar os interceptors para fazer as melhorias sugeridas.
Interceptando Requests
Os Interceptors do Axios funcionam de maneira análoga aos middlewares do Express, ou seja, você tem um processamento intermediário que acontece entre a chegada da requisição ou respostas e o retorno da mesma. Este processamento pode ser qualquer coisa que você deseje e que seja possível de implementar via JS, como por exemplo sempre adicionar um cabeçalho à sua escolha em todas requisições ou mudar o texto de uma resposta conforme o seu código.
Vamos começar com um exemplo simples, de interceptação de request, ou seja, quando você for fazer uma request, um código nosso será executado antes da mesma ser disparada para o servidor. Para isso, vamos começar criando um módulo em nosso projeto chamado BaseService.js que servirá como padrão para todas nossas chamadas via Axios.
Uma atividade comum em requests com Axios é ter de incluir um token no cabeçalho da requisição, para que a mesma seja autenticada (geralmente tokens JWT). Esse é um código simples, mas extremamente repetitivo, como abaixo. Assim, se o código será sempre o mesmo, pode colocá-lo no BaseService.js usando um interceptor de request, como abaixo.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
import axios from "axios"; axios.interceptors.request.use( config => { config.headers.Authorization = "123"; return config; }, error => Promise.reject(error) ) export default axios; |
Aqui eu peguei um valor literal, mas imagine que no lugar do 123 você pegaria de um localStorage ou cookie, por exemplo. Ou qualquer outro processamento que desejar. Em todos os serviços que você criar na sua aplicação, ao invés de carregar o Axios diretamente você deve carregar o BaseService e usá-lo, então automaticamente terá o token sendo enviado em cada request, sem a necessidade de código adicional (e repetido), como abaixo (arquivo APIService.js).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
import axios from "./BaseService"; //import axios from "axios"; const API_URL = process.env.REACT_APP_API_URL; export async function authRequest() { const body = { nome: 'LuizTools', idade: 35, uf: 'RS' }; const response = await axios({ method: 'POST', url: `${API_URL}/cadastro`, data: body, //headers: { 'Authorization': "123" } }) return response.data; } |
Repare que as ÚNICAS coisas que mudaram dessa versão para a anterior é que eu importo o BaseService ao invés do Axios e que comentei a linha dos headers. Isso causaria erro ao testar a aplicação de frontend, mas neste caso seguirá funcionando, pois os interceptors do BaseService agirão sempre antes de cada requisição, injetando o token no cabeçalho correto.
Interceptando Responses
Interceptors não funcionam apenas para requests, eles também permitem que tenhamos comportamentos padrões em cima das respostas recebidas pelos seus clientes/serviços.
Por exemplo, em muitas aplicações de frontend é comum respostas com erros de autenticação serem tratadas da mesma forma, jogando o usuário de volta para o login. Usaremos este cenário como exemplo.
Um código que testa esses possíveis erros de autenticação é visto abaixo. Facilmente ele fica se repetindo por toda aplicação.
1 2 3 4 5 6 7 8 9 10 11 12 |
function btnAuthRequestClick() { authRequest() .then(data => console.log(data)) .catch(err => { if (err.response.status === 401) window.location.href = "/login"; else console.error(err); }); } |
Para mudar isso, podemos criar um interceptor de resposta no BaseService.js, que independente do que estava sendo chamado, se recebeu um 401 ou 403, manda o usuário para login.
1 2 3 4 5 6 7 8 9 10 11 12 |
axios.interceptors.response.use( response => response, error => { if (error.response && [401, 403].includes(error.response.status)) { console.error('Redirected to login by 4xx response!'); window.location.href = '/'; } else return Promise.reject(error); }); |
Assim, todas requests que forem feitas usando o BaseService, estarão sujeitas ao processamento das suas respostas HTTP pelo interceptor acima.
Estes foram dois exemplos básicos mas que mostram o potencial dos interceptors do Axios. Com eles é possível reduzir drasticamente a quantidade de código repetido em seus clientes/serviços e com isso facilitar a manutenção do código e evitar o esquecimento de algum tratamento padrão da aplicação.
Até a próxima!
Olá, tudo bem?
O que você achou deste conteúdo? Conte nos comentários.