I have react & FastAPI being served with nginx using docker. When I run docker-compose up, everything works with the react app available at localhost:3000 and api available at localhost:8000. For instance, the get request localhost:8000/fleets/ returns the desired result. I would like the api to be reached at localhost:3000/api/ instead though.
When I add --root-path /api to the FastAPI Dockerfile, the react app still loads at localhost:3000 and I am able to reach the api at localhost:3000/api/, but my react app gets 404 errors when it tries to query for the api. When I go to the Network on tab in Chrome I see react queries for http://127.0.0.1:8000/api/fleets/ leading to a 404 error. The api still available at localhoost:8000/fleets/ though.
Should my react app be querying at localhost:3000/api/ or localhost:8000 and how do I make this adjustment?
Note I am using axios but changing axios.defaults.baseURL doesn't appear to make an impact. I currently have axios.defaults.baseURL = '/'; in my react App.jsx.
I have also removed "proxy": "http://127.0.0.1:3000" from my react package.json.
In FastAPI, I have origins = ["http://localhost:3000", "http://127.0.0.1:8000"] for dealing with CORS.
FastAPI Dockerfile:
FROM python:3.8-alpine
ENV PYTHONBUFFERED 1
WORKDIR /api
COPY requirements.txt requirements.txt
RUN pip install -r requirements.txt
COPY . .
react Dockerfile:
FROM node:15.13-alpine
WORKDIR /react
COPY . .
RUN yarn run build
nginx-setup.conf:
upstream api {
server backend:8000;
}
server {
listen 8080;
location / {
root /var/www/react;
}
location /api/ {
proxy_pass http://api/;
proxy_set_header Host $http_host;
}
}
docker-compose.yml:
version: '3'
services:
backend:
build:
context: ./api
command: uvicorn app.main:app --host 0.0.0.0 --port 8000 --root-path /api
ports:
- 8000:8000
frontend:
build:
context: ./react
volumes:
- react_build:/react/build
nginx:
image: nginx:latest
ports:
- 3000:8080
volumes:
- ./nginx/nginx-setup.conf:/etc/nginx/conf.d/default.conf:ro
- react_build:/var/www/react
depends_on:
- backend
- frontend
volumes:
react_build:
EDIT (for more details)
When running without the --root-path /api:
localhost:3000/api/fleets/does actually work.localhost:3000/api/fleetsgets redirected tolocalhost:3000/fleets/which gives nginx 404localhost:3000/api/docsgivesFailed to load API definitionerror.localhost:8000/docsworks.localhost:8000/fleets/works.localhost:8000/fleetsgets redirected tolocalhost:8000/fleets/and works.- React app pings
http://127.0.0.1:8000/fleetsand gets redirected tohttp://127.0.0.1:8000/fleets/which works.
When running with --root-path /api:
localhost:3000/api/fleets/workslocalhost:3000/api/fleetsgets redirected tolocalhost:3000/api/fleets/and workslocalhost:3000/api/docsworkslocalhost:8000/docsgives Failed to Load API definition error.http://localhost:8000/fleets/works.http://localhost:8000/fleetsgets redirected tolocalhost:8000/api/fleets/ and givesFastAPI404{"detail": "Not Found"}`
EDIT Here's a bare-bones reproducible example:
.
├── backend
│ ├── app
│ │ └── main.py
│ ├── Dockerfile
│ └── requirements.txt
├── docker-compose.yml
├── frontend
│ ├── Dockerfile
│ ├── package.json
│ ├── src
│ │ ├── App.css
│ │ ├── App.jsx
│ │ ├── App.test.js
│ │ ├── index.css
│ │ ├── index.js
│ │ ├── logo.svg
│ │ ├── reportWebVitals.js
│ │ └── setupTests.js
│ └── yarn.lock
├── nginx
└── nginx-setup.conf
./backend/app/main.py
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
app = FastAPI()
origins = [
"http://localhost:3000"
]
app.add_middleware(
CORSMiddleware,
allow_origins=origins,
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
@app.get("/day", tags=["Dates"])
async def get_day_of_week(day_num: int = 0):
"""
Get the current day of week
"""
days = ['Sun', 'Mon', 'Tues', 'Wed', 'Thur', 'Fri', 'Sat']
return days[day_num]
./backend/Dockerfile
FROM python:3.8-alpine
ENV PYTHONBUFFERED 1
WORKDIR /alpine
COPY requirements.txt requirements.txt
RUN pip install -r requirements.txt
COPY . .
./backend/requirements.txt
anyio==3.6.1
click==8.1.3
colorama==0.4.5
fastapi==0.78.0
h11==0.13.0
idna==3.3
pydantic==1.9.1
sniffio==1.2.0
starlette==0.19.1
typing_extensions==4.3.0
uvicorn==0.18.2
./frontend/src/App.jsx
import React, { useState, useEffect } from 'react';
import './App.css';
function App() {
const [error, setError] = useState(null);
const [day, setDay] = useState('Sun');
useEffect(() => {
fetch('http://localhost:3000/api/day?day_num=3')
.then((res) => setDay(res.data))
.catch((err) => setError(err.message))
}, []);
return (
<>
<>Hello</>
<>{day}{error}</>
</>
);
}
export default App;
./frontend/Dockerfile
FROM node:15.13-alpine
WORKDIR /frontend
COPY . .
RUN yarn run build
./frontend/.dockerignore
#node_modules
build
./frontend/package.json
{
"name": "frontend",
"version": "0.1.0",
"private": true,
"dependencies": {
"@testing-library/jest-dom": "^5.14.1",
"@testing-library/react": "^11.2.7",
"@testing-library/user-event": "^12.8.3",
"axios": "^0.27.2",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-scripts": "4.0.3",
"web-vitals": "^1.1.2"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"eslintConfig": {
"extends": [
"react-app",
"react-app/jest"
]
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
}
}
./nginx/nginx-setup.conf
upstream api {
server backend:8000;
}
server {
listen 8080;
location / {
root /var/www/react;
}
location /api/ {
proxy_pass http://api/;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
}
}
./docker-compose.yml
version: '3'
services:
backend:
build:
context: ./backend
command: uvicorn app.main:app --host 0.0.0.0 --port 8000 --root-path /api
ports:
- 8000:8000
frontend:
build:
context: ./frontend
volumes:
- react_build:/frontend/build
nginx:
image: nginx:latest
ports:
- 3000:8080
volumes:
- ./nginx/nginx-setup.conf:/etc/nginx/conf.d/default.conf:ro
- react_build:/var/www/react
depends_on:
- backend
- frontend
volumes:
react_build:

https://<prodhost>/api-<prodhost>seems to belocalhost:3000here (andhttp, nothttpsprobably)axios.defaults.baseURL = 'http://localhost:3000/api/';and then queryaxios.get('/fleets/')I still see the react app first queryhttp://127.0.0.1:8000/fleetsand get 307, temporary redirect tohttp://127.0.0.1:8000/api/fleets/which returns 404 not found.upstreamblock provide the port?