How to access FastAPI backend from a different machine/IP on the same local network?
Question:
Both the FastAPI backend and the Next.js frontend are running on localost
. On the same computer, the frontend makes API calls using fetch
without any issues. However, on a different computer on the same network, e.g., on 192.168.x.x
, the frontend runs, but its API calls are no longer working.
I have tried using a proxy as next.js but that still does not work.
Frontend:
export default function People({setPerson}:PeopleProps) {
const fetcher = async (url:string) => await axios.get(url).then((res) => res.data);
const { data, error, isLoading } = useSWR(`${process.env.NEXT_PUBLIC_API}/people`, fetcher);
if (error) return <div>"Failed to load..."</div>;
return (
<>
{isLoading? "Loading..." :data.map((person: Person) =>
<div key={person.id}> {person.name} </div>)}
</>
)
}
The Next.js app loads the env.local
file at startup, which contains:
NEXT_PUBLIC_API=http://locahost:20002
Backend:
rom typing import List
from fastapi import APIRouter, Depends
from ..utils.db import get_session as db
from sqlmodel import Session, select
from ..schemas.person import Person, PersonRead
router = APIRouter()
@router.get("/people", response_model = List[PersonRead])
async def get_people(sess: Session = Depends(db)):
res = sess.exec(select(Person)).all()
return res
The frontend runs with: npm run dev
, and outputs
ready - started server on 0.0.0.0:3000, url: http://localhost:3000
The backend runs with: uvicorn hogar_api.main:app --port=20002 --host=0.0.0.0 --reload
, and outputs:
INFO: Uvicorn running on http://0.0.0.0:20002 (Press CTRL+C to quit)
When I open the browser on http://localhost:3000
on the same machine the list of Person
is displayed on the screen.
When I open the browser on http://192.168.x.x:3000
on another machine on the same network, I get the "Failed to Load…" message.
When I open the FastAPI swagger docs on either machine, the documentation is displayed correctly and all the endpoints work as expected.
CORS look like this:
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=["*"],
)
Answers:
Make sure that the host
flag is set to 0.0.0.0
, when running the server— 0.0.0.0
means all IPv4 addresses on the local machine. If a host
has two IP addresses, e.g., 192.168.10.2
and 10.1.2.5
, and the server running on the host
listens on 0.0.0.0
, it will be reachable at both of those IPs. For example, through command line interface:
uvicorn main:app --host 0.0.0.0 --port 8000
or, programmatically:
if __name__ == '__main__':
uvicorn.run(app, host='0.0.0.0', port=8000)
You may also need to adjust your Firewall to allow external access to the port
you specified (by creating an inbound firewall rule for Python—on Windows, this is usually automatically created when allowing a program, Python in this case, to communicate through Windows Firewall, and by default this allows traffic on Any port
for both TCP and UDP connections).
Additionally, if your frontend is running on a separate server from the backend, please make sure to have CORS enabled and properly configured, as desribed in this answer and this answer. For example:
origins = ['http://localhost:3000','http://192.168.178.23:3000']
app.add_middleware(
CORSMiddleware,
allow_origins=origins,
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
Finally, please take a look at this answer and this answer regarding using the proper origin/URL when issuing a fetch
request from the frontend. In short, in your JavaScript asynchronous request, you should use the same domain name (but with the port
number your backend server is listening on) used for accessing the frontend in the address bar of your browser, for example:
fetch('http://192.168.178.23:8000/people', {...
Otherwise, if the domain differs, it should then be added to the list of origins in the CORS settings of the backend. For convenience, you may want to use relative paths, as suggested in a linked answer above, if both backend and frontend servers are running on the same machine).
This problem stonewalled me for days – I’m using fastapi/uvicorn on Mac, in Python 3.9.
When setting the uvicorn host to 0.0.0.0, after startup I checked and found that it only binds to TCP 127.0.0.1:
sudo lsof -PiTCP -sTCP:LISTEN
So I dug into the uvicorn code, the solution was in this file:
/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/site-packages/uvicorn/config.py
Simply change this:
sock.bind((self.host, self.port))
to this:
sock.bind((‘0.0.0.0’, self.port))
After this change I restarted uvicorn and I can access the page from any machine on my network.
Hope this helps someone!
Both the FastAPI backend and the Next.js frontend are running on localost
. On the same computer, the frontend makes API calls using fetch
without any issues. However, on a different computer on the same network, e.g., on 192.168.x.x
, the frontend runs, but its API calls are no longer working.
I have tried using a proxy as next.js but that still does not work.
Frontend:
export default function People({setPerson}:PeopleProps) {
const fetcher = async (url:string) => await axios.get(url).then((res) => res.data);
const { data, error, isLoading } = useSWR(`${process.env.NEXT_PUBLIC_API}/people`, fetcher);
if (error) return <div>"Failed to load..."</div>;
return (
<>
{isLoading? "Loading..." :data.map((person: Person) =>
<div key={person.id}> {person.name} </div>)}
</>
)
}
The Next.js app loads the env.local
file at startup, which contains:
NEXT_PUBLIC_API=http://locahost:20002
Backend:
rom typing import List
from fastapi import APIRouter, Depends
from ..utils.db import get_session as db
from sqlmodel import Session, select
from ..schemas.person import Person, PersonRead
router = APIRouter()
@router.get("/people", response_model = List[PersonRead])
async def get_people(sess: Session = Depends(db)):
res = sess.exec(select(Person)).all()
return res
The frontend runs with: npm run dev
, and outputs
ready - started server on 0.0.0.0:3000, url: http://localhost:3000
The backend runs with: uvicorn hogar_api.main:app --port=20002 --host=0.0.0.0 --reload
, and outputs:
INFO: Uvicorn running on http://0.0.0.0:20002 (Press CTRL+C to quit)
When I open the browser on http://localhost:3000
on the same machine the list of Person
is displayed on the screen.
When I open the browser on http://192.168.x.x:3000
on another machine on the same network, I get the "Failed to Load…" message.
When I open the FastAPI swagger docs on either machine, the documentation is displayed correctly and all the endpoints work as expected.
CORS look like this:
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=["*"],
)
Make sure that the host
flag is set to 0.0.0.0
, when running the server— 0.0.0.0
means all IPv4 addresses on the local machine. If a host
has two IP addresses, e.g., 192.168.10.2
and 10.1.2.5
, and the server running on the host
listens on 0.0.0.0
, it will be reachable at both of those IPs. For example, through command line interface:
uvicorn main:app --host 0.0.0.0 --port 8000
or, programmatically:
if __name__ == '__main__':
uvicorn.run(app, host='0.0.0.0', port=8000)
You may also need to adjust your Firewall to allow external access to the port
you specified (by creating an inbound firewall rule for Python—on Windows, this is usually automatically created when allowing a program, Python in this case, to communicate through Windows Firewall, and by default this allows traffic on Any port
for both TCP and UDP connections).
Additionally, if your frontend is running on a separate server from the backend, please make sure to have CORS enabled and properly configured, as desribed in this answer and this answer. For example:
origins = ['http://localhost:3000','http://192.168.178.23:3000']
app.add_middleware(
CORSMiddleware,
allow_origins=origins,
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
Finally, please take a look at this answer and this answer regarding using the proper origin/URL when issuing a fetch
request from the frontend. In short, in your JavaScript asynchronous request, you should use the same domain name (but with the port
number your backend server is listening on) used for accessing the frontend in the address bar of your browser, for example:
fetch('http://192.168.178.23:8000/people', {...
Otherwise, if the domain differs, it should then be added to the list of origins in the CORS settings of the backend. For convenience, you may want to use relative paths, as suggested in a linked answer above, if both backend and frontend servers are running on the same machine).
This problem stonewalled me for days – I’m using fastapi/uvicorn on Mac, in Python 3.9.
When setting the uvicorn host to 0.0.0.0, after startup I checked and found that it only binds to TCP 127.0.0.1:
sudo lsof -PiTCP -sTCP:LISTEN
So I dug into the uvicorn code, the solution was in this file:
/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/site-packages/uvicorn/config.py
Simply change this:
sock.bind((self.host, self.port))
to this:
sock.bind((‘0.0.0.0’, self.port))
After this change I restarted uvicorn and I can access the page from any machine on my network.
Hope this helps someone!