SPA com roteamento estático em produção

Já publicou uma SPA e ao acessar uma rota aninhada ela não funcionou? Eu passei por isso recentemente, e vou te dar algumas dicas que me ajudaram.

📑

Introdução

Ninguém gosta de passar por problemas enquanto está desenvolvendo software, mas faz parte, e aprendemos muito com os problemas que encontramos. Se tudo desse certo de primeira, teríamos descoberto muito menos do que sabemos.

Hoje vamos ver um problema corriqueiro relacionado a frontend que passei por esses dias, e como resolvê-lo.

Espero ajudar com a contribuição, pois procurei bastante para conseguir entender e encontrar uma solução correta, e gostaria de tornar o conhecimento disponível para mais pessoas.

O problema

Hoje muito se fala em SSR (Server Side Rendering), você constrói uma aplicação onde todas as rotas já existem, e o servidor envia as páginas compiladas e prontas para o cliente.

Mas não podemos nos esquecer das SPA's (Single Page Applications), que ainda são úteis para diversos modelos de negócio, onde não há necessidade de SEO (Search Engine Optimization) ou outras técnicas, as SPA's (Single Page Applications) são a melhor escolha, você não precisa lidar com problemas vindos com a escolha do SSR (Server Side Rendering), e pode construir as coisas com mais liberdade.

Sendo direto, eu tive um problema com rotas estáticas, utilizando o React Router DOM (v6.10), ao publicar minha aplicação em produção, minhas rotas não funcionavam após atualizar a aba do navegador. Quando eu acessava uma rota aninhada como site.com/categorias e clicava em recarregar ou pressionava CTRL + F5 minha aplicação quebrava e retornava um 404.

Entendendo porque isso ocorre

Um dos primeiros passos, foi tentar entender o motivo desse erro.

Aparentemente, é bem óbvio: podemos entender que, ao requisitar uma rota /alguma-coisa nosso servidor (onde o frontend está hospedado) procura alguma rota mapeada com esse endereço, e como não encontra nada, retorna o erro de Not Found (404).

Por que o nosso servidor não encontrou nada sendo que mapeei no meu roteador as rotas e quais componentes deveriam ser renderizados?

Isso é simples, na anatomia de uma Single Page Application, temos apenas um arquivo como entrada, no caso um arquivo index.html que chama um script, e injeta toda a lógica JS, fazendo assim a construção da aplicação no lado do cliente. A partir disso, o nosso router com as rotas conhecidas pode entrar em ação e fazer seu trabalho te levando para as telas mapeadas.

Logo, se tenho apenas uma entrada que é o index.html que fica na raiz / e o servidor está procurando por uma rota /alguma-coisa ele não vai encontrar nada.

Como resolver

Existem algumas formas de resolver esse problema, e depende de como sua aplicação está estruturada, quais tecnologias você usa e como seu servidor está configurado, ou o que seu servidor fornece de configuração disponível para customização.

A solução é relativamente simples, você precisa redirecionar as rotas para a raiz, e deixar que o react-router-dom se encarregue do resto.

Um primeiro passo é configurar o package.json para que nosso aplicativo saiba qual é a raiz, e para isso, vamos adicionar a chave "homepage" com o valor "/"

{
  "homepage": "/"
}

Para fazer com que o servidor redirecione para a raiz, precisamos entender onde sua aplicação está hospedada, caso seja em um servidor Apache, você pode tentar um arquivo de configuração semelhante ao abaixo, para configurar os redirecionamentos:

// Nome do arquivo: .htaccess

RewriteEngine On
RewriteBase /
RewriteRule ^index\.html$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.html [L]

Crie o arquivo com o nome .htaccess na raiz do seu repositório ou edite-o case já exista. Esse script irá pegar as requisições de rota que não foram encontradas e irá redirecioná-las para o index.html e então o react-router-dom vai tomar o controle a partir de então.

Caso você use nginx, procure o arquivo nginx.conf (normalmente em /etc/nginx/nginx.conf dentro do seu servidor, ou pode ser que esteja na raiz do seu repositório também), e adicione a seguinte configuração:

// Nome do arquivo: nginx.conf

location / {
  try_files $uri $uri/ /index.html;
}

Esse comando irá redirecionar a solicitação para /index.html caso o arquivo solicitado não seja encontrado.

Bom, cobrimos os cenários onde se tem um servidor próprio onde você faz a configuração diretamente (seja NGINX ou Apache), mas caso você utilize uma plataforma de hospedagem como netlify, vercel ou render, você terá que acessar seu painel de administração e procurar pelas configurações de redirecionamento.

Eu fiz essa configuração de maneira bem simples no render, então vou dar mais ênfase na configuração dele, mas vou deixar links das documentações da vercel e da netlify.

No render é bem simples, eu acessei a dashboard, fui no meu serviço (Static site), e ao clicar no serviço, foi aberta uma tela com o seguinte menu lateral:

render-redirect.png

Vá em Redirect/Rewrites e lá encontrará a configuração para redirecionamento.

Screenshot_4.png

Clique em Add Rule e preencha os campos

Screenshot_5.png

Você pode consultar a documentação para entender melhor como fazer essa configuração, mas o que funcionou para mim mim, e caso você esteja utilizando uma stack semelhante, irá funcionar igualmente:

Screenshot_6.png

Em Source coloque o wildcard /*, em Destination coloque a raiz /, e em Action selecione Rewrite, caso você coloque Redirect ele irá redirecionar para a raiz sempre que uma rota solicitada, e com o Rewrite a rota original não é alterada, sendo o que resolve nosso problema com o react-router-dom.

E pronto, seu frontend conseguirá manipular as atualizações de rotas em produção.

Essa configuração foi feita no render, para conferir a configuração em outras plataformas pesquise por Redirect/Rewrite, e caso queira ver as documentações do netlify e vercel, veja os links abaixo:

Futuramente posso fazer um artigo ensinando essa configuração em outras plataformas (como as citadas acima). Fique de olho no blog para acompanhar os artigos.

---

Nertec logo