reactjsnginxdocker-composevite

Proxy pass all /api to backend endpoint with React 19 (Vite)


I have an application with React 19 and FastAPI, my backend runs on localhost:8000/api/....

Here is my nginx.conf:

server {
  listen 80; # Nginx should listen on port 80 inside the container
  server_name _; # Or your actual domain if you have one

  root /usr/share/nginx/html;
  index index.html;

  # API proxy configuration (if needed)
  location /api/ {
    proxy_pass http://app:8000/; # Assuming 'app' is your backend service name in docker-compose
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
  }

  location / {
    # This is crucial for SPA routing.
    # It tries to serve the file if it exists, then the directory,
    # otherwise it serves /index.html which lets your React router take over.
    try_files $uri $uri/ /index.html;
  }

  # Optional: You can keep this, but try_files in the location block is more direct for SPA routing.
  error_page 404 /index.html;
}

My docker-compose.yml:

version: '3.8'

services:
  frontend:
    build:
      context: .
      dockerfile: Dockerfile # Referencing the Dockerfile in frontend folder
    container_name: react-frontend
    ports:
      - '5173:80' # Nginx exposes the app on port 5173
    networks:
      - backend

networks:
  backend:
    external: true
    name: task-management-network

and my docker-compose.yml for backend:

services:
  app:
    build:
      context: ./
      dockerfile: Dockerfile
    dns:
      - 8.8.8.8 # Google's public DNS
    container_name: fastapi-app
    ports:
      - "8000:8000"
    depends_on:
      - db
      - redis
    networks:
      - backend
    volumes:
      - .:/app # Mount the app directory to the container for live updates
    command: uvicorn app.main:app --host 0.0.0.0 --port 8000 --reload # Enable hot-reloading
networks:
  backend:
    name: task-management-network

My .env.production

VITE_API_BASE_URL=""
VITE_API_V1_URL="/api/v1"

My Vite config:

export default defineConfig({
  plugins: [react(), tailwindcss()],
  resolve: {
    alias: {
      '@': path.resolve(__dirname, './src'),
    },
  },
});

and in Axios config I use like this:

const baseURL = import.meta.env.VITE_API_V1_URL;

The problem is when the frontend side calls an API, instead of http://localhost:8000/api/v1/profile, it uses the base frontend URL which is not correct: http://localhost:5173/api/v1/profile (this is wrong). Does anyone know how to fix it?


Solution

  • To forward requests from /api/v1/profile to http://app:8000/api/v1/profile, you need configure the Nginx as follows:

    location /api/ {
        proxy_pass http://app:8000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
    

    This configuration ensures that the /api/ prefix is preserved in the forwarded request.

    Here are two schemes to help you connect the front-end and the back-end:

    1. Using API proxy ensures that there is no CORS problem. Just like your nginx configuration file, the final access to api is actually done by accessing nginx.

    2. Configure appropriate CORS rules and write the back-end URL directly to the front-end. This scheme is more used in the back-end load-balancing environment.

    For more details on Nginx proxying, refer to the DocumentNginx Official Document about porxy.