Usando API routes do NextJs como proxy para seu backend.

Contexto

Recentemente precisei desenvolver um frontend em Next. O projeto utiliza Auth0 para lidar com autenticação. Assim, o backend precisa receber um access token assinado pelo Auth0 para autorizar as requisições, e o front precisa enviar esse dado e saber quando o usuário está logado ou não.

Existem diversas maneiras de fazer isso, inclusive ja utilizei muito o NextAuth, ou em casos mais simples somente Context API.

No entanto, como o projeto está fortemente atrelado ao Auth0 e o respectivo SDK v2 para next saiu do beta recentemente, optei por utilizá-lo.

A ferramente é muito fácil de se utilizar e com certeza me poupou várias linhas de código.

Porém, o SDK para Next não permite que eu obtenha o access token no lado do cliente - por uma questão de arquitetura - e na própria documentação é recomendado que se utilize as API Routes como proxy para o backend.

Solução

A documentação me instrui a criar uma rota api no next para cada rota do meu backend. Em projetos extremamente pequenos, pode ser que isso faça sentido, mas no meu caso o backend dispõe MUITOS endpoints, e queria uma forma de aplicar o proxy para todos eles.

Então fiz o seguinte


// src/pages/api/projeto/[...projeto].ts

import { withApiAuthRequired, getAccessToken } from '@auth0/nextjs-auth0';
import axios, { AxiosError } from 'axios';

export default withApiAuthRequired(async function shows(req, res) {
  try {
    const endpoint = (req.query.projeto as string[]).reduce((acc, val) => acc + '/' + val,'');

    const { accessToken } = await getAccessToken(req, res, {
      scopes: ['openid','profile', 'email', 'offline_access'],
    });

    const response = await axios.request({
      method: req.method,
      data: req.body,
      params: req.query,
      headers: {
        Authorization: 'Bearer ' + accessToken
      },
      url: process.env.API_URL + endpoint
    });

    return res.status(response.status).json(response.data);
  } catch (error: any) {
    let status = 500;
    let body = {
      message: 'Internal Server Error'
    }
    if (error instanceof AxiosError) {
      status = error.response?.status!;
      body = error.response?.data!;
    }
    return res.status(status).json(body);
  }
});

Agora quando eu quero fazer uma requisição que será disparada no lado do cliente, basta eu chamar /api/projeto/{endpoint do backend}.

Um código simples que me salvou um bom tempo e esforço.

Vale lembrar que o ponto aqui não é falar sobre a SDK do Auth0, somente sobre essa estratégia, que acredito que pode ser útil para diversos outros casos.

Se alguém quiser posso fazer um post mostrando com mais detalhes a integração com o Auth0.

Se souber algum problema com essa abordagem, comenta aí abaixo.

Uma solução semelhante a isso, é utilizar o next.config.js, que ao incluir a função rewrites(), você vai ter a reescrita da colicitação. segue função:

async rewrites() {
        return [
          {
            source: '/api/:path*',
            destination: `${process.env.BACKEND_URL}/:path*`
          }
        ]
      }

em source temos a origem que será substituida, no caso estamos indicando que qualquer solicitação para /api, será reescrita, exemplo: http://localhost:3000/api/login tudo que está após /api/, será vinculado a variavel :path, que será usado no destino em destination, temos o destino, que pode ser algo fixo, ou o que recomendo que seja variavel de ambiente, conforme o exemplo. No caso do exemplo do login, internamente o processamento seria visivel ao usuario para localhost:3000/api/login, porem internamente teria sido reescrito para o backend localhost:8080/login Observe que tudo que colocar após api/ será considerado no destino, exemplo: localhost:3000/api/cadastro/cliente/novo será reescrito internamente para localhost:8080/cadastro/cliente/novo os metodos não são reescrito, então se fizer POST na rota, a reescrita também usará POST