I'm going out on a limb here, but I'm going to say it anyway: most Single Page Applications aren't RESTful. They have API contracts and know of the URL templates the server uses. When deep linking they don't need to explore the resources of the server, but can jump right in. That's convenient, but that kind of tight coupling means the SPA and the server aren't RESTful. They can't evolve independently from each other and often server and SPA releases have to be orchestrated carefully in order not to break the application.
This post looks at how a RESTful SPA would look and how that in turn makes both the server and the SPA more stable and less dependent on each other. Caveat: Of course an SPA doesn't have to be RESTful, but if you want it to be, this is one way to do it.
Deep linking and application state
My search for a RESTful SPA started with a question on Stack Overflow about how to handle deep linking in a RESTful way. There are a few options, but none of them are ideal. In the end, it boils down to how application state is persisted by the SPA and how it can be restored later on. In a traditional multi page application the current page is also the current application state. When deep linking, the browser just requests that page from the server and application state is restored.
Things are different for SPAs. URLs are mapped to components and they in turn request data from the server. The application state is not the URL in the browser, but the URL of the SPA's data request to the server is. In REST, the client (the SPA in this case) may not have advance knowledge of the server's URLs, so how would the SPA know which URL to GET?
Things are different for SPAs. URLs are mapped to components and they in turn request data from the server. The application state is not the URL in the browser, but the URL of the SPA's data request to the server is. In REST, the client (the SPA in this case) may not have advance knowledge of the server's URLs, so how would the SPA know which URL to GET?
A hybrid approach
To make the SPA RESTful, a hybrid approach should be used. This approach has the best of both worlds: The easy restoration of application state of the traditional multi page application and the responsiveness of a single page application. As an added bonus, this leads to better SEO and greater accessibility because of server side rendering, faster first page responsiveness and the application still works if JavaScript is disabled.
The hybrid approach works as follows:
1. When loading the application, the browser usually downloads the SPA as static files first and JavaScript then starts the SPA. The first big change is that the browser doesn't load the SPA first, but instead downloads an HTML version of the page directly from the server. This means that every resource (URL) of the API needs to have an HTML representation, just like a traditional multi page application. It is important that the HTML is a complete page, header, menu and footer included, so the HTML works both for the initial request and for subsequent SPA requests.
2. The HTML page loads the SPA script. The SPA intercepts all links and form submissions.
3. When a link to another page in the same site is clicked or a form is submitted, the SPA executes the request in the background. It then updates the browser URL and updates sections of the page with the HTML returned by the request.
The SPA could for example look for <section> elements in the current page that match <section> elements with a matching id in the requested page. This way, the main content area and for example the notifications area are updated by the SPA on every navigation.
An alternative is that just the main content is updated and that the SPA sets up a websocket connection to update the notifications in real-time. Because the SPA updates sections of the page rather than reloading a page entirely, the websocket connection remains intact.
The hybrid approach works as follows:
1. When loading the application, the browser usually downloads the SPA as static files first and JavaScript then starts the SPA. The first big change is that the browser doesn't load the SPA first, but instead downloads an HTML version of the page directly from the server. This means that every resource (URL) of the API needs to have an HTML representation, just like a traditional multi page application. It is important that the HTML is a complete page, header, menu and footer included, so the HTML works both for the initial request and for subsequent SPA requests.
2. The HTML page loads the SPA script. The SPA intercepts all links and form submissions.
3. When a link to another page in the same site is clicked or a form is submitted, the SPA executes the request in the background. It then updates the browser URL and updates sections of the page with the HTML returned by the request.
The SPA could for example look for <section> elements in the current page that match <section> elements with a matching id in the requested page. This way, the main content area and for example the notifications area are updated by the SPA on every navigation.
An alternative is that just the main content is updated and that the SPA sets up a websocket connection to update the notifications in real-time. Because the SPA updates sections of the page rather than reloading a page entirely, the websocket connection remains intact.
Why this approach is RESTful
In REST, there should not be tight coupling between client and server. Instead, both client and server should have an understanding of the shared media types and that's it. Compare this with the RESTful SPA: it is no longer a typed, domain specific implementation, but instead it learns to understand the HTML media type and knows how to progressively enhance the application. This makes the RESTful SPA applicable across projects with little to no modifications, potentially greatly reducing time writing client specific implementations.
A more pragmatic approach
Instead of requesting the entire page when only a partial update is required, the client can do a partial page update as described here. The server can prepare the sections to be updated by setting the URLs in data-* attributes, preventing the client from needing to know the URLs in advance.
Update: consider using htmx when building a RESTful SPA.
Update: consider using htmx when building a RESTful SPA.
Food for thought
Maybe it's time to reconsider the large SPA frameworks and libraries and build a more lightweight, more accessible, RESTful web instead.