How to connect production Vue frontend with a Python backend

Note -- you may need at least the basic Hacker plan because of the amount of disk space required to install Vue with all its dependencies and CPU seconds consummed during the installation

Disclaimer

There are multiple possible ways of using Vue with a backend framework -- steps presented below are showing one possible way of connecting those on PythonAnywhere to make rather a starting point for further adjustments.

Prerequisites

It's assumed that you have a working Node.js environment on your PythonAnywhere account -- otherwise follow steps mentioned on this help page.

Creating a scaffold Vue frontend app

Install Vue:

npm install -g @vue/cli

As a sanity check, run:

vue --version

Then, assuming your web app (Django or Flask) is at ~/mysite directory:

cd ~/mysite
vue create frontend

Django

It's assumed you created a Django web app in ~/mysite with our wizard on the Web app page. The solution presented below assumes as well that Vue takes care about the templates (and possibly routing too), so that database connections will be performed in the client (browser) via API calls. If you prefer to partially render the templates server-side (Django driven) and create templates in Django, you will need extra dependencies allowing rendering the Vue's bundle via Django (see: django-webpack-loader and webpack-bundle-tracker).

Build the Vue app

First, update the vue.config.js file in the Vue app's directory (~/mysite/frontend/) to :

module.exports = {
    ...
    publicPath: "/dist",
    outputDir: "./dist/",
    ...
};

Note that the value of publicPath will affect the routing and static files mappings (see: below) -- in our example the Vue assets will be looked for in username.pythonawhere.com/dist, and the assets will be found in ~/mysite/frontend/dists.

See also the Vue documentation for reference.

Then, still in ~/mysite/frontend, run:

npm run build

The successful build should add dist directory according to the configuration in the vue.config.js, with index.html template inside:

~/mysite/frontend $ tree -I node_modules
.
...
├── dist
│   ├── css
│   │   └── app.2cf79ad6.css
│   ├── favicon.ico
│   ├── index.html
│   └── js
│       ├── app.e82b6041.js
│       └── app.e82b6041.js.map
...

urls.py

Adjust ~/mysite/mysite/urls.py to serve the base.html template:

from django.contrib import admin
from django.urls import path, re_path
from django.views.generic import TemplateView

urlpatterns = [
    path("admin/", admin.site.urls),
    # you can put the API endpoints here
    ...
    re_path("^.*$", TemplateView.as_view(template_name="index.html")),
]

Note: the solution above, using re_path and a regex pattern for the url, assumes that all urls that are not caught by the previous urls (the admin one and, say, APIs) will be forward to Vue.

settings.py

In ~/mysite/mysite/settings.py:

Than, assuming that BASE_DIR is a pathlib.Path object (you may need to adjust the syntax if you use os.path instead), tell Django where to look for Vue generated templates (index.html in our case):

TEMPLATES = [
    {
        ...
        'DIRS': [ BASE_DIR / "frontend/dist" ],
        ...
    },
]

Static files mappings

On your web app page add static files mappings to the dist directory and reload the web app (use your username instead of "username"):

URL Directory
... ...
/dist/ /home/username/mysite/frontend/dist

You should now see the default Vue page when you visit your web app.

Flask

It's assumed you created a Flask web app in ~/mysite with our wizard on the Web app page.

Build the Vue app

Go to the ~/mysite/frontend, where we already created the Vue application and edit the vue.config.js file so it contains:

module.exports = {
    outputDir: "./dist",
    assetsDir: "static",
}

This set up will put build assets into dist/static and create the index.html template in the dist directory. See also the Vue documentation for reference.

Then run the build command in ~/mysite/frontend:

npm run build

That should create the dist directory with the structure provided in the configuration file above:

~/mysite/frontend $ tree -I node_modules
.
...
├── dist
│   ├── favicon.ico
│   ├── index.html
│   └── static
│       ├── css
│       │   └── app.2cf79ad6.css
│       └── js
│           ├── app.b7710a01.js
│           ├── app.b7710a01.js.map
│           ├── chunk-vendors.1ce598bf.js
│           └── chunk-vendors.1ce598bf.js.map
...

flask_app.py

Edit the ~/mysite/flask_app.py file to serve the default inbox.html template provided by the Vue build (make sure that the static_folder and the template_folder are not the same):

from flask import Flask, render_template

app = Flask(__name__, static_folder = "frontend/dist/static", template_folder = "frontend/dist")

@app.route('/')
def index():
    return render_template("index.html")

Static files mappings

On your web app page add static files mappings to the dist directory and reload the web app (use your username instead of "username"):

URL Directory
/static/ /home/username/mysite/frontend/dist/static

You should now see the default Vue page when you visit your web app.