Deploying a Flask + Vue app to Heroku
Tags: python, vue, devops, heroku
Published: 2020-11-15 10:07:57

In this post, I'll walk you through how to deploy a Flask and Vue app to Heroku. The final product is a super basic project you can extend to your needs.

Inspiration was drawn from [this post by Shaylan Dias](https://medium.com/@shaylandias_85978/deploying-a-flask-web-app-with-a-react-frontend-to-heroku-6168fc4ff2e5) and has been adapted to Vue.

**TLDR;** [Here's my GitHub Repo](https://github.com/ulrikson/heroku-test) and [here's the final product](https://testing-flask-vue.herokuapp.com/).

# Step 1: Create a basic Vue app
We're using [Vue CLI](https://cli.vuejs.org/guide/installation.html) for this project.
```
mkdir heroku-test
cd heroku-test

# Instantiate git
git init

# Vue CLI to create child folder
vue create client

cd client
npm start
```
Now, you'll get a localhost link in your terminal. Visit that and view your frontend.

# Step 2: Create Flask server
I assume you have pip installed for this. If not, [download pip](https://pip.pypa.io/en/stable/installing/).

```
# Create a virtual Python environment for this project
python3 -m venv .venv

# Activate the virtual env
source .venv/bin/activate

# Install dependencies
pip install flask gunicorn python-dotenv

# Create requirements.txt
pip freeze > requirements.txt

# Set up the .env file
touch .env
echo "FLASK_APP=server.app:app" > .env

# Create the server
mkdir server
cd server
touch app.py
```

Now you have to set up app.py, this is a basic script that'll do the job:
```
from flask import Flask 
from dotenv import load_dotenv
import os
load_dotenv()

# Set up the app and point it to Vue
app = Flask(__name__, static_folder='../client/dist/',    static_url_path='/')

# Set up the index route
@app.route('/')
def index():
    return app.send_static_file('index.html')

if __name__ == "__main__":
    port = int(os.environ.get("PORT", 8000))
    app.run(host='0.0.0.0', port=port)
```

When setting upp the app (`app=...`) it's crucial that it's pointed to Vues static files. When using Vue CLI, these can be found in the **dist** folder. 

# Step 3: Setup for Heroku
First, create a `package.json`and `Procfile` in the root of your project.

The Procfile will tell Heroku what processes to run.
```
# Procfile
web: npm start
```

And package.json specifies the project's dependencies:

```
# package.json
{
  "name": "heroku-flask-vue",
  "version": "0.1.0",
  "private": true,
  "dependencies": {
    "core-js": "^3.6.5",
    "eslint": "^7.13.0",
    "vue": "^2.6.11"
  },
  "devDependencies": {
    "@vue/cli-plugin-babel": "~4.5.0",
    "@vue/cli-plugin-eslint": "~4.5.0",
    "@vue/cli-service": "~4.5.0",
    "babel-eslint": "^10.1.0",
    "eslint": "^6.7.2",
    "eslint-plugin-vue": "^6.2.2",
    "vue-template-compiler": "^2.6.11"
  },
  "scripts": {
    "start": "gunicorn server.app:app",
    "build": "cd client && npm run build"
  },
  "eslintConfig": {
    "extends": "plugin:vue/recommended"
  },
  "browserslist": {
    "production": [
      ">0.2%",
      "not dead",
      "not op_mini all"
    ],
    "development": [
      "last 1 chrome version",
      "last 1 firefox version",
      "last 1 safari version"
    ]
  },
  "engines": {
    "node": "12.x"
  }
}
```


# Step 4: Create the Heroku app
Now, we have to create a Heroku app. If you don't have Heroku CLI installed, follow [these instructions](https://devcenter.heroku.com/articles/heroku-cli).

```
heroku create <name-for-your-app>

# create a remote git connection
heroku git:remote -a <name-for-your-app>

# These buildpack tell Heroku to install Python and Node
heroku buildpacks:add --index 1 heroku/nodejs
heroku buildpacks:add --index 2 heroku/python
```

# Step 5: Test locally and then deploy

```
npm run build
npm start

# If it works, deploy!
git commit -am 'Ready to deploy!'
git push heroku master
```

Heroku CLI will give you a link when your app has been deployed, follow that and voilá!

# Final
Great job! You've created a Vue + Flask app and deployed it to Heroku. Now try changing for example the `<h1>`. PS. Don't forget to run `npm run build` before pushing to Heroku again.