Django/Python

Intro to using Vue with Django

May 24th, 2021

Modern web applications usually involve both frontend (providing the user interface) and backend (providing some sort of API, for example REST or GraphQL). Since technology stacks can differ, often it's not clear how best to combine them.

This article goes through several typical integrations of Django backend and JavaScript frontend. Vue is used as the frontend framework, although the same principles apply if you're using React or another web framework.

Server-side rendering, some Vue on frontend

If the pages in your website or app are all rendered Django templates, you may want to use Vue for some additional JavaScript logic only on some pages. You can load Vue directly in those pages, similarly how you'd do it with jQuery or other JS libraries. This has the added benefit of authentification just working, since all the pages are rendered by Django. Everything you know about sessions, CSRF, and authentication, stays the same in this case.

The Vue getting-started guide has a great explanation, adapted here for Django:

{% if DEBUG %}
  <!-- development version, includes helpful console warnings -->
  <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
{% else %}
  <!-- production version, optimized for size and speed -->
  <script src="https://cdn.jsdelivr.net/npm/vue@2"></script>
{% endif %}

<div id="app-wrapper"></div>
<script>
  const app = new Vue({
    el: 'app-wrapper',
    // ... your custom logic ...
  })
</script>

If your scripts are more than a few lines of code (which is likely if the required functionality is complex enough that you need Vue), it's recommended to keep the scripts in separate .js file(s) and either load them as static files.

If you're using Vue in this way throughout your app, you can put the above snippet somewhere in your base template so it's loaded in all your pages, or organize it in any way you'd do with other Django templates. But if most of your UI is handled with Vue, you may consider taking one of the next approaches.

Single Page Application (SPA)

In contrast to the preivous approach, if you're building an entire frontend as single JavaScript application (usually called single-page apps or "SPA"), you'll want Vue to handle everything in the browser and use Django to provide some kind of API for it (usually REST or GraphQL). The question then is how to best integrate the two parts of your system.

Project structure

Backend and frontend can live in separate directories and you can even handle each as separate project, although for smaller projects, it makes sense to keep them both in same repository.

If both backend and frontend are in the same repository, a good directory structure might be:The easiest thing to do is not to integrate backend and frontend at all. They can live in separate directories and you can even handle each as separate project, although for smaller projects, it makes sense to keep them both in same repository.

If both backend and frontend are in the same repository, a good directory structure might be:

- project
    - frontend
        (everything Vue related goes here)
    - backend
        (everything Django related goes here)

To create a new project in backend, you can use API Bakery to create a new project with database models and API endpoints in a few minutes, or use django-admin to create an empty Django project (assuming Python virtual environment is already activated) and set up your APIs manually:

cd project
django-admin startproject backend
cd backend
# set up the Django project manually ...

Next, let's initialize the Vue project (assuming vue-cli is already installed):

cd project
vue create frontend

This gives us the above project structure. With that in place, let's focus on the integration.

Separate projects

The simplest set-up is not to integrate backend and frontend at all. Django and Vue are completely separate and don't know about each other. You'll just need to tell Django to allow CORS requests from frontend, and tell your front-end code where the API lies.

CORS is a way to limit or allow JavaScript from accessing resources from other domains. Since we'll be running two separate servers (Django for backend, Vue devserver for frontend), the front-end JavaScript will need to access the API on another domain (even if they're on the same machine, if port differs, that counts as another domain).

To do that, you'll need to tell Django to serve CORS headers. You can use django-cors-headers Python package with the following settings:

CORS_ORIGIN_ALLOW_ALL = True
CORS_ALLOW_CREDENTIALS = True
CORS_ALLOW_HEADERS = default_headers + ("content-disposition",)

You'll also need to add corsheaders to your INSTALLED_APPS and corsheaders.middleware.CorsMiddleware to MIDDLEWARE in the settings file.

If you've created your Django backend with API Bakery, all of this is already set for you.

After CORS is set up, the only thing you need to do is to use the URL to your Django server (for example http://localhost:8000 while developing) when accessing the API from JavaScript.

This approach works great if you have completely separate API and frontend projects and if API and the HTML and JavaScript will be served from different servers. The drawbacks are that session-based authentification won't work, since sessions are confined to one host. You'll need to use token-based auth instead. Another drawback is that you need to make sure you configure the backend API URLs correctly each time you deploy or start the front-end part.

Vue proxies Django

If your application is a Vue single-page app, like in the previous example, but you need tighter integration and/or want to serve it from the same place as the API, you can place Django behind Vue. This means Vue dev server will serve all requests from your browser except the API. Assuming all the API is located under one (or a few) URLs on your server, this can be done quite easily.

First, create a project with frontend and backend structure and initialize Django and Vue, like in the previous example.

Next, create file vue.config.js in the root of your Vue installation (that's project/frontend/ in our case), with the following contents:

module.exports = {
  devServer: {
    proxy:  {
      '/api': {
        target: 'http://localhost:8000'
      },
    }
  }
}

This instructs the Vue server to forward any requests to /api route to the Django server running on port 8000. You can add more targets, for example if you also want to expose DRF Browsable API, Swagger UI or Django admin interface. Read more about devServer configuration in the Vue documentation site.

Now your JavaScript code doesn't need to hardcode the base URL of the API - it knows it'll be on the same server. What about common configuration? You can have a static API endpoint that returns the config, so you don't need to duplicate it. You can even use session-based authentification again - sessions work because frontend and backend are on the same host.

But what if you'd like to have some URLs served by Django (for example, user sign up flow) and use the Vue as SPA on a single URL within your server?

Server-side rendering + SPA combo, Django proxies Vue

For some projects it makes sense to have Django in front, providing server-side rendered pages using the usual Django templating features, but still use full power of Vue on certain pages. This is how we develop API Bakery. Most of the site is plain ol' Django, except the project editor. Since the editor is a complex beast and we want to make use of babel and transpiling, we build it as a separate Vue project instead of using the first approach outlined in this guide.

For API Bakery we've glued the two together with a bit of custom code, but if you don't want to do everything by yourself, there's django-webpack-loader which does exactly that (the project was unmaintained for a while but as of spring 2021 is again in active development). However setting it up is a bit more complex and outside of the scope of this article. Using WebPack transparently with Django provides more info. Although it's a few years old and uses React as an example, it's still relevant even if you're using Django and Vue in 2021.

Going further

The information presented here is just scratching the surface. If you want to dig deeper, definitely check out Definitive Guide to Django and Webpack (a 7-part series in progress), and Integrating a Modern JavScript Pipeline into a Django Application (also available as a recorded talk with slides).

These resources are not Vue-specific but are applicable to any JavaScript and webpack-based frontend setup.