Fastapi: [QUESTION] Correct default route usage

Created on 4 Nov 2019  路  3Comments  路  Source: tiangolo/fastapi

Default Routes for SPA's -- what's the correct way to do them

Suppose you have an SPA where https://example.com/foo/bar/spam should be routed in JavaScript, not in Python.

As far as I can tell, the "correct" way to do that seems something like this:

async def default_route(scope, receive, send):
    request = Request(scope, receive=receive, send=send)

    response = templates.TemplateResponse("spa.html", {'request': request})
    await response(scope, receive, send)

app.router.default = default_route

That seems a little dirty however, since it's jumping outside both FastAPI and Starlette's documentation and just setting default to a function that then recreates the request object. It isn't very FastAPI-y.

Is there perhaps another, alternative way to establish a default route?

question

Most helpful comment

@tiangolo's answer sounds like the way to go in production.

For testing/development I'm using a separate static resource router that sits behind all my api routers, but before my static files mount.

 @router.get('/{resource}', include_in_schema=False)
    def static(resource: str):
        if "." not in resource:
            return FileResponse(path=path.join(os.getenv("STATIC_RESOURCES_PATH"), "index.html"))
        else:
            return FileResponse(path=path.join(os.getenv("STATIC_RESOURCES_PATH"), resource))

This basically serves any non-API request that is one level deep (i.e /home, /about, ...). If the path contains a . it serves the static file as resource and otherwise serves the index.html page. Any request for deeper paths (i.e. containing /s) will fall back to the original static files router.

Aware this won't work for all scenarios, but it's a good enough heuristic for my use case, but it would be nice if FastAPI would feature a generic fallback router.

All 3 comments

What I would suggest, and what I would do by default, is to put a load balancer on top of both backend (FastAPI) and frontend (probably served by Nginx). I would use Traefik as it handles HTTPS certificates with renewals easily, but you could also use Nginx, HAProxy or something similar.

Then I would make it pass everything under /api to the FastAPI app and the rest (/) to the Nginx frontend. And then the Nginx frontend would be configured to pass everything that is not a frontend file to just the same index.html so that the frontend router (React-router, Vue router, Angular) can take it from there. E.g. as it's done in https://github.com/tiangolo/node-frontend

@tiangolo's answer sounds like the way to go in production.

For testing/development I'm using a separate static resource router that sits behind all my api routers, but before my static files mount.

 @router.get('/{resource}', include_in_schema=False)
    def static(resource: str):
        if "." not in resource:
            return FileResponse(path=path.join(os.getenv("STATIC_RESOURCES_PATH"), "index.html"))
        else:
            return FileResponse(path=path.join(os.getenv("STATIC_RESOURCES_PATH"), resource))

This basically serves any non-API request that is one level deep (i.e /home, /about, ...). If the path contains a . it serves the static file as resource and otherwise serves the index.html page. Any request for deeper paths (i.e. containing /s) will fall back to the original static files router.

Aware this won't work for all scenarios, but it's a good enough heuristic for my use case, but it would be nice if FastAPI would feature a generic fallback router.

Then I would make it pass everything under /api to the FastAPI app and the rest (/) to the Nginx frontend.

I'm not OP, but this is the exact step I'm working on right now. I have nginx set up to serve my static files, but I'm not sure how to route /api to my FastAPI app. What ports should my fastapi container listen to and what's the line in the nginx conf look like?

Thanks

Was this page helpful?
0 / 5 - 0 ratings