Python Version
3.9.5
Django Version
4.0.2
Package Version
6.0.0
Description
There are many SO posts in which folks ask "How do I configure Django to serve my index.html
when I visit routes handled by a frontend router?" (such as react-router
, react-router-dom
, or Angular's router). For example, when I visit myDjangoURL.com/app/frontendRoute
, how do I serve a file in app/build/index.html
that loads a React/Angular app with a browser-based router that handles the /frontendRoute
route?
This, this, this, and this all have answers that suggest configuring Django wildcard URLs/paths and returning something like django.shortcuts.render(request,'index.html')
or django.views.generic.TemplateView
. Of course django.contrib.staticfiles.views.serve
is not available in production (when django.conf.settings.DEBUG
is False
), but everyone asking this question really wants a function like serve
, and not a function like render
(maybe the implementation of these functions is similar enough that it doesn't matter, but the function names suggest something is wrong). "Serving" one single static file as a rendered template is probably okay performance-wise if Whitenoise is managing and serving all the built dependencies linked to in index.html
.
I think it creates a few problems, though:
- Users might have to add their static build folder to their
django.conf.settings.TEMPLATES
, where it doesn't belong, possibly causing template name conflicts (because this build folder may not be managed in the same way as a Python app)
- Using
render
might cause weird behavior considering Javascript frameworks might use some of the same syntax as Django's/Jinja's templating language (e.g. {{variablename}}
might cause a templating/rendering error).
- Obviously the
index.html
file would not be a version-hashed copy in the static folder, which could fail to update in caches somewhere downstream, defeating the purpose of Whitenoise's versioning efforts for this one file.
Whitenoise already supports some "interface sugar" by serving an "index" file (WHITENOISE_INDEX_FILE = True
in Django) when the static file root is served. Considering the proliferation of frontend routing (evidenced by the StackOverflow posts I linked to above), I think you should expose a frontend routing interface to be used in Django's settings. I think this interface could look something like the following:
WHITENOISE_FRONTEND_ROUTES_BASE = '/app/' # defaults to STATIC_URL
that automatically adds a catchall route that serves index.html
for any route starting with '/app/
or
WHITENOISE_FRONTEND_ROUTES_BASES = [('/app/', 'app/index.html'), ('/webapp/' , 'webapp/index.html')] # for multiple frontends served by one Django/Whitenoise app
The interface would probably need some way to both include and exclude certain routes/filepaths, and Whitenoise might want to add a quick check to ensure none of these routes conflict with existing static content routes (and if so, maybe it automatically changes file and directory names in the STATIC_ROOT
folder so that static content can still be served without this issue).
I don't 100% understand how Whitenoise does its thing, but it makes the case for replacing Nginx unless you know how to configure everything according the best practices. This reverse-proxying configuration seems like a very easy concern compared to everything else Whitenoise does, and I think it seems wrong for users (like me) to set up Nginx for the sole purpose of it having config options to allow frontend routing.
If this is already possible in a first-class supported way using Django/Whitenoise, please close the issue but please explain what I'm missing (and what the others on the SO posts are missing). Please note that if this is supported, I don't think it's clear from the documentation.