Fix Railway ECONNREFUSED error by unifying backend URL resolution
Problem: Next.js frontend in production mode falls back to http://backend:8000 (Docker Compose internal hostname) when BACKEND_URL env var is not set. In Railway's distributed environment, this hostname doesn't exist, causing ECONNREFUSED errors. Solution: Create unified getBackendUrl() function with consistent fallback priority across all server-side proxying files: 1. Explicit BACKEND_URL (for Railway / custom deployment) 2. Development mode -> http://localhost:8000 3. NEXT_PUBLIC_API_URL (may be set in Railway) 4. Docker Compose default -> http://backend:8000 Changes: - New: frontend/lib/backend-url.ts - centralized URL resolution - Updated: frontend/next.config.ts - use getBackendUrl() - Updated: frontend/app/api/chat/route.ts - use getBackendUrl() - Updated: frontend/app/api/auth/google/token/route.ts - use getBackendUrl() Railway users must set BACKEND_URL=https://<backend-service>.up.railway.app Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
69eba15bbb
commit
ffc36edb97
|
|
@ -3,14 +3,11 @@
|
||||||
* This proxies the token exchange to the backend
|
* This proxies the token exchange to the backend
|
||||||
*/
|
*/
|
||||||
import { NextRequest, NextResponse } from "next/server";
|
import { NextRequest, NextResponse } from "next/server";
|
||||||
|
import { getBackendUrl } from "@/lib/backend-url";
|
||||||
// Use backend internal URL if available (Railway), fallback to public URL
|
|
||||||
const BACKEND_URL = process.env.BACKEND_URL ||
|
|
||||||
process.env.NEXT_PUBLIC_API_URL ||
|
|
||||||
"http://localhost:8000";
|
|
||||||
|
|
||||||
export async function POST(request: NextRequest) {
|
export async function POST(request: NextRequest) {
|
||||||
try {
|
try {
|
||||||
|
const backendUrl = getBackendUrl();
|
||||||
const body = await request.json();
|
const body = await request.json();
|
||||||
const { code, redirect_uri } = body;
|
const { code, redirect_uri } = body;
|
||||||
|
|
||||||
|
|
@ -22,7 +19,7 @@ export async function POST(request: NextRequest) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Forward to backend
|
// Forward to backend
|
||||||
const backendResponse = await fetch(`${BACKEND_URL}/api/auth/google/token`, {
|
const backendResponse = await fetch(`${backendUrl}/api/auth/google/token`, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,9 @@
|
||||||
import { NextResponse } from "next/server";
|
import { NextResponse } from "next/server";
|
||||||
|
import { getBackendUrl } from "@/lib/backend-url";
|
||||||
|
|
||||||
export async function POST(req: Request) {
|
export async function POST(req: Request) {
|
||||||
try {
|
try {
|
||||||
const isDev = process.env.NODE_ENV === "development";
|
const backendUrl = getBackendUrl();
|
||||||
const backendUrl =
|
|
||||||
process.env.BACKEND_URL ||
|
|
||||||
(isDev ? "http://localhost:8000" : "http://backend:8000");
|
|
||||||
|
|
||||||
// Read the complete body from the request
|
// Read the complete body from the request
|
||||||
const bodyText = await req.text();
|
const bodyText = await req.text();
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,27 @@
|
||||||
|
/**
|
||||||
|
* Unified backend URL resolution for server-side proxying.
|
||||||
|
*
|
||||||
|
* Priority:
|
||||||
|
* 1. BACKEND_URL – explicit override (Railway / custom deployment)
|
||||||
|
* 2. Development mode – http://localhost:8000
|
||||||
|
* 3. NEXT_PUBLIC_API_URL – fallback (often set in Railway for client-side too)
|
||||||
|
* 4. Docker Compose default – http://backend:8000
|
||||||
|
*
|
||||||
|
* Used by next.config.ts rewrites and Next.js API route handlers.
|
||||||
|
*/
|
||||||
|
export function getBackendUrl(): string {
|
||||||
|
if (process.env.BACKEND_URL) {
|
||||||
|
return process.env.BACKEND_URL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (process.env.NODE_ENV === "development") {
|
||||||
|
return "http://localhost:8000";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (process.env.NEXT_PUBLIC_API_URL) {
|
||||||
|
return process.env.NEXT_PUBLIC_API_URL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Docker Compose internal hostname (only works when both services share a Docker network)
|
||||||
|
return "http://backend:8000";
|
||||||
|
}
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import type { NextConfig } from "next";
|
import type { NextConfig } from "next";
|
||||||
|
import { getBackendUrl } from "./lib/backend-url";
|
||||||
|
|
||||||
const nextConfig: NextConfig = {
|
const nextConfig: NextConfig = {
|
||||||
output: 'standalone',
|
output: 'standalone',
|
||||||
|
|
@ -56,13 +57,7 @@ const nextConfig: NextConfig = {
|
||||||
},
|
},
|
||||||
|
|
||||||
async rewrites() {
|
async rewrites() {
|
||||||
// In development: use localhost backend
|
const backendUrl = getBackendUrl();
|
||||||
// In production (Railway): use BACKEND_URL env var or fallback to Railway URL
|
|
||||||
const isDev = process.env.NODE_ENV === 'development';
|
|
||||||
// Default to http://backend:8000 in production (Docker) if not set
|
|
||||||
const backendUrl = process.env.BACKEND_URL ||
|
|
||||||
(isDev ? "http://localhost:8000" : "http://backend:8000");
|
|
||||||
|
|
||||||
console.log(`[Next.js] Rewriting API requests to: ${backendUrl}`);
|
console.log(`[Next.js] Rewriting API requests to: ${backendUrl}`);
|
||||||
|
|
||||||
return [
|
return [
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue