5

Firstly, I just wanted to say that this is my first web application project. I've spent the past few days trying to find answers on how to essentially put the frontend and backend together. I have a lot of questions, but the main one I want answered is on how to return my frontend 'final product' from a backend endpoint.

This is what I understand (please correct me if I'm wrong):

  • The frontend code is run by the client (browser).
  • When the client interacts with the webpage, the frontend makes API calls to the backend to retrieve/modify data, as necessary.
  • The backend and frontend is often developed separately, and could be hosted on separate servers.
  • It is, however, possible (and maybe simpler) to host it on a single domain/server. I am hoping to do this, in order to avoid a whole set of issues with CORS.

Then comes the following problem:

When I want to test out my front end and see how it's coming along, I just run npm run start. I then go to the given url (usually http://localhost:8080/) and I have access to the frontend that I've developed. And when I want to deploy it, I run npm run build, which gives me a dist folder (bundled together and minified).

If I want to run and test my backend locally, as I am using FastAPI, I simply run uvicorn main:app --reload.

How to put the two together? More specifically, in my backend code, how do I return the product of my frontend work (i.e., the dist folder?). I've tried the following (simplified):

@app.get("/", response_class=HTMLResponse)
def root():
    return open("../frontend/dist/index.html", "r").read()

but, of course, this only gives me the static html without the React components.

I realize this post may be loaded with incorrect assumptions and poor practices (in which case, my apologies! and I would appreciate any corrections/suggestions.) However, if the following questions could be answered, I would greatly appreciate it. These are questions I have that will hopefully help me test my whole web application locally on my computer.

  1. How do I return the product of my frontend work for the GET request at the domain root endpoint?
  2. If there is a page A, page B, and page C for my web app, each with url www.example.com/A, www.example.com/B, and www.example.com/C do I have to create three separate React frontend projects? I.e., equivalent of having three dist folders? What is the standard way this is handled?
hainabaraka
  • 363
  • 1
  • 3
  • 9
  • I encourage you to use my simple function https://stackoverflow.com/a/70065066/12234006 – Angel Nov 22 '21 at 11:33

3 Answers3

3

The "traditional" approach to running a web application is to have a server that serves your web application (i.e. your React app). Usually you'll hear about nginx being used as the web server being used for modern day single page applications. When you run npm run start you start up a local server on your machine and it makes your app available at http://localhost:8080 (the port and hostname are of course configurable).

When it comes to your API, it should be it's own server available at a different endpoint/url, and then your web app will make API calls to that endpoint/url in order to fetch data.

The way you're describing things, it sounds like you're trying to use FastAPI to server render your web app, but I'm not sure how feasible that is, especially considering there is an entire framework solely dedicated to server rendering react applications.

Robert Cooper
  • 2,022
  • 1
  • 6
  • 19
2

These are good questions and it is certainly possible. I will tell you what I do, with the caveat that there may be a better way...

I'm using Vue instead of React, but its build process also sends static html, js and css to a dist/ directory, so the process should be about the same.

First you can copy the dist/index.html file you mention into your FastAPI templates/ directory. You will use your FastAPI route to serve that file as a Template.

Then copy your js and css into a static/ directory and make sure FastAPI knows about both static and templates.

from fastapi import FastAPI, Request
from fastapi.staticfiles import StaticFiles
from fastapi.templating import Jinja2Templates

app = FastAPI()

app.mount("/static", StaticFiles(directory="static"), name="static")

templates = Jinja2Templates(directory="templates")

@app.get("/")
async def serve_spa(request: Request):
    return templates.TemplateResponse("index.html", {"request": request})

You may need to set something in React in order for your build to know that the js and css will live in a dir called static. For Vue, there is the assetsDir option within vue.config.js

For your question about handling different paths, like example.com/a and example.com/b, it depends how you want to handle those requests. Are you wanting your single react app to handle all of those routes?

If that is the case, you may want to see also: How to capture arbitrary paths at one route in FastAPI?

One option is to copy the serve_spa() route above and handle your routes, like /a, /b, etc.

Or use a catch-all route:

@app.route("/{full_path:path}")
async def catch_all(request: Request, full_path: str):
    print("full_path: "+full_path)
    return templates.TemplateResponse("index.html", {"request": request})
csum
  • 1,420
  • 9
  • 15
1

I've also struggled with this and here's an approach that worked for me

import logging

from fastapi import FastAPI
from starlette.responses import RedirectResponse
from starlette.staticfiles import StaticFiles

app = FastAPI()


@app.get("/")
async def index():
    return RedirectResponse(url="/index.html")

app.mount("/", StaticFiles(directory="backend/ui/"), name="ui")

Note: Please observe that app.mount is called after registering the / route. From my experience if the mount is called before the registration then it will simply not redirect to index.html

Note: I use react-scripts to generate the app.