Last Updated: June 2021
However, most Django projects start from one of two common archetypes, which we'll dub server-first and client-first.
Let's look at these in more detail.
The server-first architecture
A server-first architecture is usually chosen—if you can call it "choosing"—by a backend developer. It's the most common Django setup and the natural way a Django application typically evolves.
The progression to server-first goes something like this:
First you learn Django. You go through the tutorial, learn about models and views, and get your very first "hello world" application up and running. You're on a roll!
Then, inevitably, you want to do something that requires a little front-end interaction. Maybe a modal dialog, some real-time form validation, or a mobile menu. It doesn't matter.
And it works great. Still on a roll!
Then, over time, there are more modals and more menus and more pages. You start doing more complex things like managing state, dynamically rendering components, and sharing code across templates.
So you end up feeling stuck. Knowing there's a better solution out there, but not sure exactly how to achieve it. Congratulations—you've found your way to server-first Django architecture! Don't worry, you're not alone.
Problems with Server-First architectures
Like we saw above, server-first architectures typically work great at the start, but become difficult to maintain as more front-end code gets added over time.
Here are some common problems with server-first applications:
- They may have front-end logic duplicated in several places instead of using shared functions and libraries
Not every project will have all of these issues, but most will have at least some of them!
Okay, well this sounds like a bit of a mess. So let's consider a different set up entirely.
Enter, the client-first architecture.
The client-first architecture
The opposite of the server-first architecture is—naturally—client-first.
Client-first architectures are often—though not exclusively—chosen by front-end developers. And unlike the ad-hoc path to a server-first setup described above, client-first architectures require a deliberate intention from the start of the project.
Meanwhile, the backend is a completely separate, standalone Django project where Django is mostly just an API into the database. Client-first projects almost always make heavy use of Django Rest Framework.
The end result looks something like this:
This architecture can work great for developers who really do just want to use Django as an API—or large teams with specialized front-end and back-end developers.
However, client-first setups come with substantial tradeoffs.
As a result, simple tasks can become much more complicated in a client-first setup.
For example, building a page to update a data model will typically involve creating a
Serializer class, an API endpoint, and custom front-end code to render a form and handle validation when
all you really wanted was a 5-line ModelForm.
If Django's philosophy is "batteries included" then a client-first architecture is more like BYOB: bring your own batteries.
Problems with Client-First architectures
Client-first architectures can work great. For the right project and the right team they provide a setup that is clear, organized, and scales well.
However, a client-first architecture will often come with these drawbacks:
- They are unable to leverage a lot of Django's value: views and templates, forms, the built-in login interface, etc.
- Implementing simple pages can be unnecessarily difficult and complex
- They require a non-intuitive paradigm shift for classical Django developers to do anything that touches the front end
- They don't work with many useful Django packages that haven't been designed API-first
- They can have more complex development, build and deployment setups
As a result, development in a client-first architecture can be slower and more tedious than in a traditional server-first model—especially when you're first getting your application off the ground.
So this isn't exactly the panacea we were looking for either. Dang.
Comparing server-first and client-first set ups
Let's take stock of where we are so far. The following table summarizes some of the key differences between client-first and server-first architectures.
|Our Mascot||The Spaghetti Monster, which is what your front-end code looks like||The Energizer Bunny, because you're bringing all your own batteries|
|Chosen by||Back-end developers going full-stack||Front-end developers going full-stack
Larger teams with separate back-end and front-end developers
|How it is created||Haphazardly over time as front-end code is needed||
|Key Advantages||Intuitive to Django developers
Leverages built-in Django features
Clean separation of back end and front end
|Key Disadvantages||Front-end code gets unwieldy
|Forces re-implementing many Django features in the front end
Simple tasks are more difficult
So which of these do you choose?
Why does this have to be a choice at all?
Well, turns out it doesn't.
That's where the hybrid architecture comes in.
Enter the hybrid architecture
The hybrid architecture is the well-constructed lasagna to your server-first front-end spaghetti. It's the batteries you always wished you had in your client-first Django back end. It's the... ok—bad analogies aside—it's the best of both worlds.
The key concept is that in a hybrid architecture, rather than choosing between client-first or server-first at the project level, we choose it at the page level.
For some pages—login, static content, simple UIs—we rely primarily on a server-first setup and let Django carry the load. For other pages where we need a complex front end we lean heavily on client-first principles. And for everything in between we rely on a sane toolchain that allows us to mix and match Django with a front-end codebase that is clear and sensible to navigate.
Before getting into how to achieve this magic panacea, let's first take a look at some examples.
A hybrid architecture in practice
Here's an example of a hybrid architecture in practice from a real-world application created by the author of this guide: this site. I know, meta, right?
Remember, in a hybrid application we'll have all of the the following types of pages:
- Everything in between
Let's look at some examples of each of these.
Server-first Django pages
Pure Django pages are the simplest and most straightforward to set up since they have basically zero front-end code. In many applications these are far-and-away the most common.
Want an example of a server-first page? You're on one!
This article is written in markdown, rendered by python-markdown and Django's template engine for your reading pleasure.
In addition to the guides—most of this site fits into the server-first category, including the landing page, user sign up flow, and most of what you see post-login.
It's quite common for server-first pages to dominate any particular web application. They're fast, simple, easy to develop, and often all that's needed.
By using a server-first architecture for these basic components, you can increase the speed of development and ease of maintenance of your project and let Django to do what it's best at—create rock-solid web applications without reinventing the wheel.
HEY’s UI is 100% HTML over the wire. We render plain-old HTML pages on the server and send them to your browser encoded as text/html. No JSON APIs, no GraphQL, no React—just form submissions and links.— Sam Stephenson (@sstephenson) June 15, 2020
Still, not everything fits into our server-first world—if it did, this entire series wouldn't need to exist!
Our client-first pages adopt a very similar setup to the client-first architecture described above, only at the page level.
So next we'll walk through an example client-first page hosted on this site—a basic page that allows you to create, update, and delete a sample data model of employees.
In order to access the example, you'll need to first create an account on this site. This is because the example uses data associated with your Django User object. Don't worry—if you'd rather not do this, you can still follow along with the screenshots!
To access the demo, first create an account and then then visit this link. The demo is a client-first page made with React. There is also a Vue version. You won't be able to tell the difference, since they do the exact same thing.
When you first load the page you see the following:
However, clicking "Create Employee" doesn't actually take you to a new page but instead immediately renders a form where you can enter employee details.
Again, clicking "Add Employee" immediately takes you back to a list view, where you can see the newly created employee and more options.
Behind the scenes, on this page Django is basically rendering the following template and letting React/Vue take over from there.
There are a lot of details we're glossing over for now—including exactly how Django hands things off to the front end and how information flows between the two sides. Don't worry, we'll come back to these later!
One other thing you might have noticed is that the object demo sits inside the global layout and navigation of the larger site. This is another benefit of hybrid applications—you can use your existing Django template hierarchy and still make single page apps inside it.
The in-between pages
One example of this you might find in a SaaS application is a subscription page—where a customer can sign up to a subscription on your site using Stripe—a payment gateway. The subscription demo page is available without an account so if you skipped setting one up above you can still follow along.
This page looks simple, but there's a fair amount of front-end code trickling in.
For example, clicking on a plan needs to move the highlighted blue box and check mark, as well as update the price at the bottom. Likewise, toggling between "Annual" and "Monthly" updates all the prices as well as the text below it. And of course, the Stripe-managed credit card form needs to handle a payment flow via Stripe's libraries.
Pegasus is the fastest and easiest way to launch a new project with Django.
These in-between pages can be tricky to deal with. If we're not careful we can easily end up back in the "spaghetti monster" server-first world.
So a key part of our hybrid architecture will be coming up with a way to manage hybrid pages like this one in a way that doesn't lead to front-end spaghetti. We'll cover all of that in depth in Parts 3 and 4.
Subscribe for Updates
Sign up to get notified when I publish new articles about building SaaS applications with Django.
I don't spam and you can unsubscribe anytime.