As vezes rodar seu reverse proxy local, pode ajudar
Github repo: https://github.com/vNNi/nodejs-reverse-proxy
No último mês, trabalhei na performance do site do Magalu, com o objetivo de aumentar o score do Core Web Vitals.
E um dos pontos principais que temos hoje, é a inclusão de muitos scripts de terceiros (marketing e analytics).
Esses scripts acabam onerando a performance do site. Então, o que fizemos, foi utilizar
a lib Partytown no carregamento desses códigos externos
fora da Main Thread. Futuramente irei escrever sobre esse processo e os ganhos.
Uma das features da lib Partytown, é buscar os scripts de terceiros utilizando uma
request HTTP do browser (fetch), porém, em muitos casos, em um erro de CORS e o
recomendado é a utilização de um reverse proxy!.
Então, para testes locais (e validar se o uso da lib de Partytown), criei um servidor local que local como Reverse Proxy. E por não ter encontrado nenhuma referência funcional e simples, resolvi escrever aqui.
ref: https://github.com/vNNi/nodejs-reverse-proxy/blob/main/fastify-reverse-proxy.mjs
.js
import { Agent } from "undici";
import Fastify from "fastify";
import cors from "@fastify/cors";
import { encodeContent } from "./encode-content.mjs";
const fastify = Fastify({
logger: true,
});
await fastify.register(cors, {});
fastify.get("/proxy", async function handler(request, reply) {
const targetUrl = decodeURIComponent(request.query?.target || "");
let parsedUrl;
if (!targetUrl) {
return reply.code(400).send({ error: "should has target url for proxy" });
}
try {
parsedUrl = new URL(targetUrl);
} catch (error) {
parsedUrl = null;
}
if (!parsedUrl) {
return reply.status(204).send();
}
try {
const urlFinal = parsedUrl.toString();
const agent = new Agent({
connect: {
keepAlive: true,
rejectUnauthorized: false,
},
});
const currentHeaders = new Headers({ ...Object(request.raw.headers) });
const response = await fetch(urlFinal, {
dispatcher: agent,
method: request.method,
mode: "no-cors",
headers: currentHeaders,
});
const data = await response.text();
if (response.headers.get("content-encoding")) {
/**
* we should encode to return...
* ref: https://nodejs.org/api/zlib.html
*/
// encodeContent(response, data)
}
// this doesn't work...
// on: http://localhost:3000/proxy?target=https%3A%2F%2Fanalytics.tiktok.com/i18n/pixel/static/main.MTc5M2Y0YjUwMQ.js
response.headers.forEach((value, key) => {
/**
* Here we need (for while) we need to ignore
* encoding or instead encode de data with same
* encoding algorithm, otherwise we receive the error: ERR_CONTENT_DECODING_FAILED
* https://kinsta.com/knowledgebase/err_content_decoding_failed/
*/
if (["content-encoding"].includes(key)) {
return;
}
reply.header(key, value);
});
reply.header("Content-Type", response.headers.get("content-type") || "");
return reply.status(response.status).send(data);
} catch (error) {
return reply.status(500).send({ error: "true" });
}
});
try {
await fastify.listen({ port: 3000 });
} catch (err) {
fastify.log.error(err);
process.exit(1);
}Rodando servidor:
.sh
node fastify-reverse-proxy.mjsTestando:
.sh
curl "http://localhost:3000/proxy?target=https://analytics.tiktok.com/i18n/pixel/static/main.MTc5M2Y0YjUwMQ.js"Conteúdo do arquivo está aqui:
https://github.com/vNNi/nodejs-reverse-proxy/blob/main/next-js-api-handler.mjs
E a estrutura final do projeto deve ficar:
.sh
|--/src
|--/api
|--proxy.tsO uso é igual ao do com Fastify:
.sh
curl "http://localhost:3000/proxy?target=https://analytics.tiktok.com/i18n/pixel/static/main.MTc5M2Y0YjUwMQ.js"