Docs

Next.js route handler

createX3ProxyHandler from @3x-engine/sdk/next/proxy implements the proxy protocol with the Fetch API only (Request in, Response out). It works in Next.js route handlers (Node and Edge runtimes) and in any framework that speaks Fetch (Astro, SvelteKit, Remix, Hono, Bun, Deno).

Setup

npm install {{API_ORIGIN}}/v1/sdk/3x-engine-sdk-0.1.0.tgz
.env.local
X3_API_ORIGIN={{API_ORIGIN}}
{{PROXY_KEY_ENV}}=sk_3x_YOUR_PROXY_KEY
app{{PROXY_PATH}}/[...path]/route.ts
import { createX3ProxyHandler } from "@3x-engine/sdk/next/proxy";

export const { GET, POST, OPTIONS } = createX3ProxyHandler({
  apiOrigin: process.env.X3_API_ORIGIN!,
  proxyKey: process.env.{{PROXY_KEY_ENV}},
});

Point the SDK at the path – <X3Analytics host="{{PROXY_PATH}}" …/>, init({ host: "{{PROXY_PATH}}" }) or data-host="{{PROXY_PATH}}".

What the handler does:

  • forwards only /v1/e, /v1/collect, /v1/c/*, /v1/cb and /v1/s/* (everything else → 404);
  • strips cookie and authorization, forwards user-agent, origin and the body;
  • sets x-3x-client-ip from x-forwarded-for / x-real-ip, x-3x-client-country from x-vercel-ip-country / cf-ipcountry, and x-3x-proxy-key;
  • caches scripts (public, max-age=3600, stale-while-revalidate=86400).

Without proxyKey the handler still forwards, but the API ignores the forwarded IP – visitors behind the proxy are then merged. Always set the key in production.

If the site has middleware.ts / proxy.ts, exclude the path from its matcher, and do not name the folder with a leading underscore (private in the App Router).

Verify

curl -s -o /dev/null -w "%{http_code}\n" https://YOUR-SITE{{PROXY_PATH}}/v1/s/x3.js
curl -s -o /dev/null -w "%{http_code}\n" https://YOUR-SITE{{PROXY_PATH}}/v1/c/{{PUBLISHABLE_KEY}}

Both must print 200. Then load a page: the network tab shows POST {{PROXY_PATH}}/v1/e → 204. A 403 on that request with a correct origin means the proxy key is wrong, revoked or belongs to another project.

Other runtimes

// Hono / Bun / Deno – mount the same handler
import { createX3ProxyHandler } from "@3x-engine/sdk/next/proxy";

const proxy = createX3ProxyHandler({ apiOrigin: process.env.X3_API_ORIGIN!, proxyKey: process.env.X3_PROXY_KEY });
app.all("/n3/*", (c) => {
  const method = c.req.method as "GET" | "POST" | "OPTIONS";
  return proxy[method] ? proxy[method](c.req.raw) : new Response(null, { status: 405 });
});

On Vercel the incoming x-forwarded-for is set by Vercel itself and safe to use. On a plain Node server behind your own load balancer, make sure the balancer overwrites x-forwarded-for – a client can otherwise spoof its first entry.