I have a backend REST API written in Python, and I want to make a simple front end in Svelte. The front end should be a Single Page Application and not have to rely on a Node.js server, or any kind of server side routing. I want to serve this over Nginx and rely on client side routing. The front end is "dynamic" in the sense that it needs pages with unknown parameters, for example:
/blog/1
/blog/2
However, it is not quite intuitive as to whether you need to prerender and which build adapter to use. Without the correct settings, the build will most likely fail. Even if you get the build to run successfully, it is hard to test it on your local host.
The key to making this work is to add a fallback
page in svelte.config.js
; without this you won't be able to build successfully:
import adapter from '@sveltejs/adapter-static';
const config = {
kit: {
adapter: adapter({
fallback: 'index.html'
})
}
};
Next you need to disable server side routing and prerendering, as well as enable client side routing. In your src/routes/layout.js
, add the following:
export const ssr = false;
export const csr = true;
export const prerender = false;
You can actually prerender certain content. But any page with [parameters] must not be prerendered.
Now you can run npm run build
.
In your build
directory, you should now see an index.html
file:
To test it, you would ideally set up an Nginx on docker. However to quickly test it you can use Python's simple server.
On the command line navigate to the build
directory and run:
python -m http.server
Now in your browser, go to http://localhost:8000/
. Python's web server will default this /
to your index.html.
However, if you try to directly load another route, like http://localhost:8000/foo
, you will see a 404. To rectify this, run the following Python code from within the build
file and try again:
import os
from http.server import SimpleHTTPRequestHandler, HTTPServer
class FallbackHTTPRequestHandler(SimpleHTTPRequestHandler):
def send_error(self, code, message=None, explain=None):
if code == 404:
self.path = "/index.html"
super().do_GET()
else:
super().send_error(code, message, explain)
httpd = HTTPServer(("localhost", 8000), FallbackHTTPRequestHandler)
httpd.serve_forever()
What this will do is always serve the /index.html
file as a fallback.
Without the fallback
option in svelte.config.js
, you can see errors when you run npm run build
such as:
> Using @sveltejs/adapter-static
@sveltejs/adapter-static: all routes must be fully prerenderable, but found the following routes that are dynamic:
- src\routes/
- src\routes/bar
- src\routes/bar/[lol]
- src\routes/foo
This error is because adapter-static will require all routes to be prerenderable, unless you add a fallback.
Another error you may see if you mark your routes as prerenderable:
Error: The following routes were marked as prerenderable, but were not prerendered because they were not found while crawling your app:
- /bar/[lol]
In this case, you have a dynamic route, so prerendering won't work.
The solution for SPAs is to use a fallback in svelte.config.js
.