I’m setting up routing in my first Elm SPA, and I’m wondering how folks are handling 404s. I can get get my Nothing route working great and it can say, “404 Not Found”, but the actually http response code is still saying that everything is fine.

My backend has some routes at /api/ but everything else it serves a file with my elm app, so my server doesn’t handle 404 and routing, only the app. Also I don’t use Maybe in this context, but a NotFound section that is default when my router can’t match any other.

Your SPA navigates internally and reaches a page that doesn’t exist. (Probably due to a broken link inside your app.)
In this case, since it’s an internal navigation, there is no HTTP request to the server and therefore no HTTP response code at all. So there can’t be any 200 or 404 or anything else.

Your user does a fresh load of the page on a URL that doesn’t match any route that is defined by your SPA.
In this case you are indeed making an HTTP request and will get a response. But the back-end doesn’t know what is a valid route and what’s not, so it can only really respond with 200.
It’s the SPA code that decides that it doesn’t recognise the route and so it displays your NotFound page. But at that stage it’s too late for a 404. The HTTP request has already happened and was successful. The fact that the page is “missing” was determined by the SPA code, not the server. So it has nothing to do with HTTP and a HTTP 404 response would not be appropriate.

So I guess this might be a case where there’s a bit of a mind shift required when working with SPAs compared to server-rendered apps. The concept of a “missing page” is separate from the concept of HTTP 404 because “page” is not necessarily something that is loaded over HTTP at all.

jachin:

Or maybe it’s not that important?

I think this might be the right point of view for this. It’s worth asking whether your users can even tell the difference. I think in most apps they can’t.

The first thing that comes to mind is search engines. If you care about search engines and SEO, you’ll want to return the correct status codes.

(On the other hand, if you don’t particularly care about SEO for your app, I think it’s safe to ignore status codes.)

That said, most companies which are selling a product want good search results. I’m curious how companies using Elm handle status codes? With React Router, you can run the routing logic on the client and server without too much difficulty, which allows the server to return the correct status code. Elm doesn’t have an official way to run routing logic on the server, so I suspect you’ll find landing pages and main websites are more often written with server-side rendering, while web applications are written in Elm.

We have duplicate routing on the server side so that we can send 404s for obviously invalid routes, but for stateful routes you have to make some tradeoffs.

Say you have a route like /content/{some uuid}
You can validate the UUID quickly, but verifying that piece of content with that UUID exists in a database is slow.

For users we prefer speed over the status code so we only validate the UUID and they end up seeing a “Not Found” page in the app with a 200 status code.
For bots we prefer correctness so we validate UUID and then make sure the content exists before returning.

Meta tags come into this as well, and they have to be done on the server too, it would be great to be able to server-render them and transfer some initial state to the client!

It is possible to re-use server rendered DOM for the app if you are using Browser.element, but it is due to an implementation detail and probably shouldn’t be relied on, when you’re using Browser.application it creates a new body node, replacing the existing one, but with Browser.element it diffs the node you give it and applies patches. If you can seed the initial state through flags and match the server state you can avoid the flash you would get when the server rendered DOM is replaced by Elm’s.