feat: implement dynamic API proxying to FastAPI backend

- Added a new route handler for dynamic API requests, allowing proxying to the FastAPI backend.
- Removed the previous rewrite configuration in next.config.ts for cleaner integration.
- Updated .env.example to clarify backend URL usage.
This commit is contained in:
Anish Sarkar 2026-04-22 05:44:03 +05:30
parent e86d279d55
commit 9ecccc5403
3 changed files with 65 additions and 16 deletions

View file

@ -1,7 +1,6 @@
NEXT_PUBLIC_FASTAPI_BACKEND_URL=http://localhost:8000
# Server-only. Internal backend URL used by Next.js server code.
# Falls back to NEXT_PUBLIC_FASTAPI_BACKEND_URL when unset.
FASTAPI_BACKEND_INTERNAL_URL=https://your-internal-backend.example.com
NEXT_PUBLIC_FASTAPI_BACKEND_AUTH_TYPE=LOCAL or GOOGLE

View file

@ -0,0 +1,65 @@
import type { NextRequest } from "next/server";
export const dynamic = "force-dynamic";
const HOP_BY_HOP_HEADERS = new Set([
"connection",
"keep-alive",
"proxy-authenticate",
"proxy-authorization",
"te",
"trailer",
"transfer-encoding",
"upgrade",
]);
function getBackendBaseUrl() {
const base = process.env.FASTAPI_BACKEND_INTERNAL_URL || "http://localhost:8000";
return base.endsWith("/") ? base.slice(0, -1) : base;
}
function toUpstreamHeaders(headers: Headers) {
const nextHeaders = new Headers(headers);
nextHeaders.delete("host");
nextHeaders.delete("content-length");
return nextHeaders;
}
function toClientHeaders(headers: Headers) {
const nextHeaders = new Headers(headers);
for (const header of HOP_BY_HOP_HEADERS) {
nextHeaders.delete(header);
}
return nextHeaders;
}
async function proxy(
request: NextRequest,
context: { params: Promise<{ path?: string[] }> }
) {
const params = await context.params;
const path = params.path?.join("/") || "";
const upstreamUrl = new URL(`${getBackendBaseUrl()}/api/v1/${path}`);
upstreamUrl.search = request.nextUrl.search;
const hasBody = request.method !== "GET" && request.method !== "HEAD";
const response = await fetch(upstreamUrl, {
method: request.method,
headers: toUpstreamHeaders(request.headers),
body: hasBody ? request.body : undefined,
// `duplex: "half"` is required by the Fetch spec when streaming a
// ReadableStream as the request body. Avoids buffering uploads in heap.
// @ts-expect-error - `duplex` is not yet in lib.dom RequestInit types.
duplex: hasBody ? "half" : undefined,
redirect: "manual",
});
return new Response(response.body, {
status: response.status,
statusText: response.statusText,
headers: toClientHeaders(response.headers),
});
}
export { proxy as GET, proxy as POST, proxy as PUT, proxy as PATCH, proxy as DELETE, proxy as OPTIONS, proxy as HEAD };

View file

@ -44,21 +44,6 @@ const nextConfig: NextConfig = {
},
},
// Proxy /api/v1/* to the FastAPI backend. Keeps the real backend host
// out of the client bundle. FASTAPI_BACKEND_INTERNAL_URL is server-only.
async rewrites() {
const target =
process.env.FASTAPI_BACKEND_INTERNAL_URL ||
process.env.NEXT_PUBLIC_FASTAPI_BACKEND_URL ||
"http://localhost:8000";
return [
{
source: "/api/v1/:path*",
destination: `${target.replace(/\/+$/, "")}/api/v1/:path*`,
},
];
},
// Configure webpack (SVGR)
webpack: (config) => {
// SVGR: import *.svg as React components