Building SPA-like Apps with Django and Inertia.js
The main objective of this tutorial is to help new developers joining the team understand the purpose of Inertia.js and its important role as part of the development stack within the company.
InertiaJS allows us to create applications that operate operationally as a SPA (single-page application) without all the complexity of having to build a full SPA, such as developing APIs, CORS, JWT Authentication, Routing, State Management, among others.
InertiaJS is framework agnostic, meaning it can be used in any server-side and client-side framework. In other words, we could use any web framework like Django, Laravel, Rails, and we could also use any client-side framework that supports dynamic components such as Vue, ReactJS, or Svelte.
Next, we will illustrate the process and flow of an application using Django, InertiaJS, and Vue.
Server-side rendering
First, it is worth noting that we no longer perform template rendering in the traditional way with Django. Instead, we use a single base.html template, which contains a simple div, which will be used as the root container of VueJs. Basically, they have a structure like the following (several tags are omitted for simplicity):
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<div id="app"></div>
</body>
</html>
Then, in Django views, instead of rendering a template as we would in the traditional server-side MVC development, we will return this base template with the name of the component we are going to render from the frontend and the initial data we are going to pass to it (a.k.a Props).
The following is a simplified code of a Django view using this pattern, pay attention to the render_inertia function as this is what makes all the magic happen:
from . import models, serializers
from inertia.views import render_inertia
def transactions(request):
# Get all the transactions for the logged user
transactions_obj = models.Transaction.objects.all(
user_wallet__user=request.user
).order_by("-created")
# Serialize the transactions
transactions_schema = serializers.TransactionsSchema(many=True)
# Dump the transaction objects to a JSON Object
transactions = transactions_schema.dump(transaction_list)
props = {
"transactions": transactions,
}
return render_inertia(
request,
"Transactions",
props=props,
template_name="base.html"
)
The great thing about the above approach is that it allows you to use the standard routing and views (controllers) of such a powerful framework as Django, with access to the database through the ORM without the need to use an API, while maintaining many of the benefits of using a modern JavaScript framework such as Dynamic components, Webpack, ESLint, among others.
Note
It should be noted that we can still create APIs in Django, but this happens because it is a requirement of the application and is not mandatory at the beginning of any development.
How Inertia.js works
To illustrate how inertia works, let’s assume we have an application with 2 basic routes: /first-page
and /second-page
,
below we show how a flow of an application made standard with Inertia would be:
1. In the first load of /first-page
, we render the base.html
template that we saw in the Server-side rendering section,
and the component that will later be loaded into the root div of the application, this includes the name of the base component and its data (props).
2. When a user clicks on a link, these links will be a special type of component called an Inertia Link <inertia-link href="/second-page" />
,
this component intercepts the click event, to prevent the default behavior of the browser (which would be to perform the load directly from the framework from the server-side),
and instead of this, an XHR type GET request or popularly known as “ajax” is created towards the URL of the link, in this case, the URL of this link points to /second-page
.
3. On the Django server, the Inertia Middleware detects that this XHR request is actually an Inertia request. To detect this, Inertia Link sets the field X-Inertia
in the HTML
headers, and this causes instead of returning the base.html template, it returns a JSONResponse, which contains the component of the next page to be loaded and its props in a
JSON object.
4. On the client-side, Inertia dynamically performs the change of root components and instantiates it, passing the props that were returned from the server in response to the XHR request.
Once the new page is loaded, Inertia updates the web browser history using push or replace state.
Below is a more comprehensive diagram of how InertiaJS works when used with Django and VueJS: