How to Build Scalable Websites with Wagtail and Nuxt

Bootstrap a website with Wagtail, Nuxt, Graphene and MinIO

devs group
7 min readMay 13, 2020

The task: Building a website. As web developers, as common as binding your shoes. However you don’t only bind it, but rather craft it.
Naturally you first need to know: Is the shoe for hiking or climbing, an accessory or a sandal? Since you can’t build a shoe that suits all feet and occasions — right?

Well we don’t know much about shoes, with Websites however we strive for maximum flexibility! To make a shoe to climb the alps as well as to dominate in a sprint, what you need is the right material, or to put it precisely: the right software Stack.

So how to choose what to work with? Here is our reasoning:

  • Mature technologies: To guarantee stable functionality, we choose tested tools.
  • Promising tech: We make sure our tools always stay sharp.
    A vibrant open-source community ensures an active development. An accompanying corporate involvement can be a steady motor,
    but can also bind us to their choices: We won’t risk stability over breaking changes, so we look for a good mix.
  • State of the Art: To keep in sync with today's demands we optimize for performance, search-engine-optimization (SEO) and security.
  • Compatibility: We build well playing teams, also in our stack and minimize Overhead.
  • Scalability: We prepare for growth, ensure potential to transition into more demand-intensive realms seamlessly.
  • Agility: We always preserve the ability to react to changes in the web landscape by making services attachable and detachable.

It is worth elaborating the last point a little bit, since it presents a basic assumption to the approach described here.
Some technologies deliver you an all-in-one solution from frontend to backend while they are really only good at a part of the job.
Furthermore web-development is a sprawling industry with unforeseeable changes around every corner, if a tool ever does fall behind the times, we don’t want to rebuild everything from scratch.

That is why we prefer a headless approach, it provides us with a reusable Stack that allows for implementation of the above criteria while leaving us in charge to optimize and adapt it, forging our very own Swiss Army knife of website development. Here is how we do it.

The Tools: CMS

Content Management Systems (CMS) provide an interface for editors and work in conjunction with the backend to store content in the database.

Our choice: Wagtail

[…] It’s important to be aware of which hats you’re wearing at any moment, and to use the right tools for that job. —Zen of Wagtail

This Quote from wagtails philosophy shows its division of work mindset making it a perfectly suitable candidate for our approach.
Wagtail is a feature-rich but lightweight CMS and due to its decision to provide an admin-panel interface for editing (rather than frontend-editing) it plays along well in a headless environment.

Being built on Django it inherits the stability and security of a well tested platform. It is open-source and benefits from an active community and multi-company backing.

The Tools : Frontend

The fancy looks that are facing the visitors of the website.

Our choice: Nuxt / Vue

Nuxt.js is a Framework based on Vue.js that provides flexibility through automatic configuration. It basically unleashes the potential of Vue so that
features like Server Side Rendering, Code Splitting and Static Site Generation are easily implemented or changed. Since sites are pre-rendered per default this brings a huge SEO boost at very low effort.
Underlying Vue brings one of the most approachable interfaces and fastest growing community support, making this a future-proof setup that does not get in our way.

The Tools : API

In a headless setup with distinct front- and backends the frontend needs a definitive way to query for data from the backend, an interface.
Typically those are designed as a REST interface.

Our choice: GraphQL

Instead of REST we access data via GraphQL. As a query language developed by facebook its future relevance is guaranteed. We use its python (as in Django) implementation, graphene-python, which is developed under an open-source license.
The possibility to abstract arbitrary backends behind it ensures operability in the face of possible changes to the stack in the future, thus aligning with our goals of Agility and Compatibility.
Due to the flexibel nature of a query in GraphQL, client-side demands can be addressed without making changes to the backend and over- and underfetching vanishes allowing for an additional boost of flexibility and efficiency.

The Tools : Object Store

Not all data is structured, so unstructured Objects (like photos and videos) are stored in a cloud storage server.

Our choice: MinIO

The #1 open source object store. And the most enterprise ready. —

This claim by MinIO is backed up by the overwhelming number of prominent users they have within the Fortune 500 Companies, including Apple and JPMorgan. We choose MinIO because we treat file service as a backing service. This allows for more flexibility and system stability since possible data issues can be mitigated by spinning up backup-systems to switch to.

The MinIO cloud storage server is designed to be minimal and scalable. Should exceptionally high demands arise its compatibility with Amazons S3 API enables transitioning the code to AWS seamlessly.

The Build: Bringing It All Together

Note: To ensure portability, we code in containers. Docker is a necessity.

Maintaining a working configuration enables us to act fast and bootstrap websites with minimal effort. We host a stripped-down copy at github, that's where configuration details can be found. Here is how to use it.

We start by cloning into the public repository of our base-setup and adapt it according to our new project.

git clone
mv base-wagtail <New-Projectname>
cd <New-Projectname>
git remote rm origin
git remote add origin <New-Repo>

The root directory is layed out like this:

cms: This folder contains the code for the django backend and the wagtail admin panel.

web_frontend: This directory contains the nuxt setup.

Next we showcase a simple mock site, that lets you choose a card from a list.

The Build: Prepare The Backend

To have something to work with, we add a new app cards in the cms folder, register it with wagtail in the and add some models to it:


This will give us entries in the wagtail admin panel to add content. The wagtail docs are exhaustive about what goes on in detail.

Since we don’t rely on wagtails page tree and often choose basic Django models wagtail will not automatically pick them up to show them in the interface. We can easily tell wagtail to do so, though:

And thats it, now pointing a browser to localhost:8000/admin we see wagtails slick admin interface and we can input some content:

wagtail admin interface

The Build: Create The API

Now with data in the backend, we can fire up graphene in nuxt to fetch it for us. First we have to tell graphene about it:


We import the models we created earlier and created nodes for them. We defined really basic resolve functions, two for the infocard model to be able to fetch a single one that has been clicked as well as a list of all of them to render on the homepage.

The Build: Render Frontend

Nuxt has created a clean directory structure for us and expects instructions to render the homepage in pages/index.vue:


After initiating the apollo client we are able to access the data via the resolve functions we created. This allows us to feed the v-for loop with titles of the infocards. Only the titles were fetched not other fields of the class, this is due to the flexible nature of the GraphQL API.

This flexibility induces the necessity of a flat caching concept. It is important to always return the id property to mitigate the possibility of caching problems with graphene.

The nuxt-link tag should bring us to a route /entry/${} the way that nuxt works is that it searches for that site in pages/entry/_id.vue, so here we go, we’ll also create an InfoCard component:

web_frontend/pages/entry/_id.vue & web_frontend/components/InfoCard/index.vue

The Result: Start The Website

Time to let the bird fly. First we provide Nuxt with everything it needs.

cd /project_root/web_frontend
yarn install

After making sure the containers can pick up everything they need from the environment we start them up.

cd /project_root
cp .env.dist .env
docker-compose up -d

If everything goes well and there is data in the database, a browser at localhost:3000 now shows us a simple list. Clicking on one item will open up a crudely depicted card with a title some info text and possibly an image.

While everything is not stylish yet, we just completed the whole cycle of site creation and by doing that we now not only own a slick set of tools but know how to use it.


Author: Vincent Trötschel is a physicist by academic education and a programmer by his own. He joined the devs-group in early 2020.