Closed estheruary closed 5 months ago
I think we tried to introduce the "fallback_file" with a different name some time ago... 🤔
I prefer the changes to be atomic, yes. And I think there's a discussion, or PR about your first point - you might want to check that.
For anyone using Starlette / FastAPI running into the same issue:
app = FastAPI()
class SPAFiles(StaticFiles):
def lookup_index(self):
return super().lookup_path("index.html")
def get_directories(
self, directory: PathLike | None = None, packages: list[str | tuple[str, str]] | None = None
) -> List[PathLike]:
directories = []
if directory is not None:
directories.append(directory)
for package in packages or []:
if isinstance(package, tuple):
package, statics_dir = package
else:
statics_dir = "statics"
spec = importlib.util.find_spec(package)
assert spec is not None, f"Package {package!r} could not be found."
assert spec.origin is not None, f"Package {package!r} could not be found."
package_directory = os.path.normpath(os.path.join(spec.origin, "..", statics_dir))
directories.append(package_directory)
return directories
def lookup_path(self, path: str) -> Tuple[str, os.stat_result | None]:
path, stat = super().lookup_path(path)
if path == "" and stat is None:
return self.lookup_index()
return path, stat
app.mount(
"/app",
SPAFiles(
# If your webapp lives along side your server you can use packages as well.
# Let's say your SPA was in project_root/myapp/webapp and when you ran
# npm build it output to the build/ directory inside there. You would specify
# that like the following.
#packages=[("myapp.webapp", "build")],
directory="path/to/my/directory",
html=True,
follow_symlink=True,
),
name="static",
)
Summary
There are two changes supporting one aim, if you don't consider that to be atomic I can open two PRs.
fallback_file
option which is used in client side routing. These tools expect that any path that doesn't resolve to a file in the build directory will return the main SPA file (typicallyindex.html
).Checklist
Fixes https://github.com/encode/starlette/discussions/1821
An example of someone in the wild wanting this kind of functionality: https://stackoverflow.com/questions/63069190. I think the examples you find around the web to be insufficient in the case where
index.html
is part of your static bundle that might live in a package.