Flask integration

How to use Vue with Flask - free starter

This guide will provide you with a free template for a Flask application, with MySQL database and Vue + Bootstrap 5 front-end.


Prerequisites

Before starting the project make sure to install the following utilities:


Creating a new Flask application

Let's create a fresh Flask application so that we can go through all the steps together.

Step 1

Creating MySQL database.

In order to create a new database you need to run the following command:

        
            
          mdb database init -db mysql8
      
        
    
  • Create a new user
  • Provide username, password, database name and description.

CLI will display your username, password, database name and connections string. Now you can go to phpMyAdmin where you will be able to handle the administration of the MySQL database.

Note: the password must contain at least one uppercase letter, one lowercase letter, one number, one special symbol and have minimum length of 8.

Important: Do not close your terminal window until you save your credentials somewhere. This is the only time we will show you your database password. If you won't save it you'll loose it.

Step 2

Create a table.

Go to phpMyAdmin, log in, and add a table. For example on purpose of this tutoria with name tasks. Then create a three columns: id (type: int), name (type: text) and description (type: text).

Step 3

Create Flask project by creating an Python envrionment. Apply these commands into terminal:

        
            
        mkdir myproject
        cd myproject
        py -3 -m venv venv
      
        
    
        
            
        mkdir myproject
        cd myproject
        python -3 -m venv venv
      
        
    

Step 4

Now activate created environment. In case that you are working on Windows and command is not working, try this: source ./venv/Scripts/activate. After this you should see venv catalogue and in your terminal will be visible (venv) badge.

        
            
        venv/Scripts/activate
      
        
    
        
            
        . venv/bin/activate
      
        
    

Step 5

Install Flask.

        
            
        pip install Flask
      
        
    

Step 6

Create a flaskr folder (in your root directory) and then put inside a __init__.py file with this basic structure.

        
            
        from flask import Flask

        def create_app():
          app = Flask(__name__)
        
          return app
      
        
    

Step 7

Install Flask MySQL library for handling MySQL database.

        
            
        pip install flask_mysqldb
      
        
    

Step 8

Edit __init__.py file by adding MySQL database configurations - insert it into create_app function.

        
            
      from flask_mysqldb import MySQL

      def create_app():
        ...
        mysql = MySQL(app)
        app.config['MYSQL_HOST'] = 'mysql.db.mdbgo.com'
        app.config['MYSQL_USER'] = 'your_usernam'
        app.config['MYSQL_PASSWORD'] = 'your_password'
        app.config['MYSQL_DB'] = 'your_database_name'
      
        
    

Step 9

Install Flask CORS library for handling CORS.

        
            
        pip install flask_cors
      
        
    

Step 10

Edit __init__.py file by adding these lines preventing CORS errors - insert it into create_app function.

        
            
        from flask_cors import CORS, cross_origin

        def create_app():
          ...
          cors = CORS(app)
          app.config['CORS_HEADERS'] = 'Content-Type'
          @cross_origin()
      
        
    

Step 11

Import jsonify and request packages from Flask and add routes for handling requests coming from your frontend part.

        
            
        from flask import Flask, jsonify, request
        from flask_cors import CORS, cross_origin
        from flask_mysqldb import MySQL

        def create_app():
          ...
          @app.route("/tasks", methods=['GET'])
          def getTasks():
              cur = mysql.connection.cursor()
              cur.execute("SELECT * from tasks")

              tasks = cur.fetchall()

              tasksList = []
              for task in tasks:
                  taskDict = {
                  'name': task[0],
                  'description': task[1],
                  'id': task[2],
                  }
                  tasksList.append(taskDict)
              return jsonify(tasksList)


          @app.route("/task", methods=['POST'])
          def createTask():
              name = request.json['name']
              description = request.json['description']
              _id = request.json['id']
              
              cur = mysql.connection.cursor()

              cur.execute(
                  "INSERT INTO tasks (name, description, id) VALUES (%s, %s, %s)",
                  (name, description, _id),
              )
              mysql.connection.commit()
              cur.close()

              return "Data has been sent"

          @app.route("/edit", methods=['PUT'])
          def updateTask():
              name = request.json['name']
              description = request.json['description']
              _id = request.json['id']
              
              cur = mysql.connection.cursor()

              cur.execute(
                  "UPDATE tasks SET name = %s, description = %s WHERE id = %s",
                  (name, description, _id),
              )
              mysql.connection.commit()
              cur.close()

              return "Data has been updated"

          @app.route("/delete/<taskId>", methods=['DELETE'])
          def deleteTask(taskId):
              
              cur = mysql.connection.cursor()

              cur.execute(
                  "DELETE FROM tasks WHERE id = %s",
                  [taskId],
              )
              mysql.connection.commit()
              cur.close()

              return "Data has been removed"
      
        
    

Step 12

Start your Flask API.

        
            
        flask --app falskr run
      
        
    

Creating MDB Vue application

Note: Don't forget to go back to your root folder before next step. Folders flaskr, venv and mdb5-free-vue should be in the same directory.

Step 1

Create a new vite project with our MDB starter. Run the command below and select MDB5 Free Vue starter.

        
            
            mdb init
        
        
    

Your folder structure should look like this

        
          flask/
          ├── flaskr
          ├── mdb5-free-vue
          └── venv
        
      

Step 2

Let's make some changes to the created vue app. First we need to install axios inside our mdb5-free-vue directory.

        
            
          npm install axios
      
        
    

Remove style.css file (don't forget to delete it from main.ts file) and remove HelloWorld file from components directory.

Step 3

Let's create a .env file inside a mdb5-free-vue directory. We have to add VITE_ before the name of your variable, because it's the only way to expose them to Vite-processed code. Don't forget to change URL to the one you created earlier.

        
            
          VITE_API = "LINK_TO_YOUR_BACKEND_APP
      
        
    

Step 4

Add new content to Home.vue file inside the views directory.

Since our starter database contains some sample models and routes, let's use them. We will create an application that will show a list of tasks. We also intend to create a functonality for adding new tasks, changing their content and removing them.

We have already prepared the code for you, so go ahead and copy and paste it into App.vue.

Ok, so what's actually going on there. We use MDB components, MDBBtn, MDBModal, MDBListGroup, MDBInputs and few other. The modal will be responsible to show inputs that will allow you to add, edit and send tasks to the database. The Manage tasks button, gives possibilty to modify or remove tasks. At the end, the list group will display our data.

        
            
        <template>
          <MDBContainer class="mt-5">
            <MDBRow class="pt-5">
              <MDBCol class="text-center">
                <MDBBtn color="primary" @click="taskModal = true">Add task</MDBBtn>
              </MDBCol>
            </MDBRow>
            <MDBRow class="mt-3 p-5" style="min-height: 40vh">
              <MDBCol class="d-flex justify-content-center align-items-center">
                <div v-if="taskList.length === 0">
                  Nothing to display. Add a few tasks.
                </div>
                <MDBListGroup v-else class="list-group-light" style="min-width: 22rem">
                  <MDBListGroupItem
                    class="d-flex justify-content-between align-items-center gap-5"
                    v-for="task in taskList"
                    :key="task.id"
                  >
                    <div>
                      <div class="fw-bold">
                        {{ task.name }}
                      </div>
                      <div class="text-muted">{{ task.description }}</div>
                    </div>
                    <div>
                      <MDBIcon
                        class="text-primary me-3"
                        title="edit"
                        icon="pen"
                        style="cursor: pointer"
                        @click="() => editModal(task)"
                      />
                      <MDBIcon
                        class="text-danger"
                        title="delete"
                        icon="trash"
                        style="cursor: pointer"
                        @click="() => deleteTask(task.id)"
                      />
                    </div>
                  </MDBListGroupItem>
                </MDBListGroup>
              </MDBCol>
            </MDBRow>
          </MDBContainer>
          <MDBModal
            id="addNewTaskModal"
            tabindex="-1"
            labelledby="addNewTaskModalLabel"
            v-model="taskModal"
          >
            <MDBModalHeader>
              <MDBModalTitle id="exampleModalLabel">{{
                isEdited.edited ? "Edit task" : "Add task"
              }}</MDBModalTitle>
            </MDBModalHeader>
            <MDBModalBody>
              <form>
                <div class="my-4">
                  <MDBInput
                    label="Name"
                    type="text"
                    v-model="newTaskName"
                    counter
                    :maxlength="60"
                  />
                </div>
                <div class="my-4">
                  <MDBInput
                    label="Description"
                    type="text"
                    v-model="newTaskDesc"
                    counter
                    :maxlength="255"
                  />
                </div>
              </form>
            </MDBModalBody>
            <MDBModalFooter>
              <MDBBtn
                color="secondary"
                @click="
                  {
                    resetInputs();
                    taskModal = false;
                  }
                "
                >Close</MDBBtn
              >
              <MDBBtn
                color="primary"
                @click="handleSaveChanges"
                :disabled="!canSendData"
                >{{ isEdited.edited ? "Save changes" : "Add task" }}</MDBBtn
              >
            </MDBModalFooter>
          </MDBModal>
        </template>
      
        
    
        
            
        <script setup lang="ts">
          import { ref, onMounted, computed } from "vue";
          import {
            MDBContainer,
            MDBRow,
            MDBCol,
            MDBListGroup,
            MDBListGroupItem,
            MDBBtn,
            MDBModal,
            MDBModalTitle,
            MDBModalHeader,
            MDBModalBody,
            MDBModalFooter,
            MDBInput,
            MDBIcon,
          } from "mdb-vue-ui-kit";
          import axios from "axios";
          
          interface SingleTask {
            id: number;
            name: string;
            description: string;
          }
          
          const taskList = ref<SingleTask[]>([]);
          const taskModal = ref(false);
          const newTaskName = ref("");
          const newTaskDesc = ref("");
          const isEdited = ref({ edited: false, value: -1 });
          const API_URL = ref("");
          
          const canSendData = computed(() => {
            if (newTaskName.value.trim() === "" || newTaskDesc.value.trim() === "") {
              return false;
            }
            return true;
          });
          
          const resetInputs = () => {
            newTaskName.value = "";
            newTaskDesc.value = "";
            isEdited.value = { edited: false, value: -1 };
          };
          
          const handleSaveChanges = async () => {
            if (!canSendData.value) {
              return;
            }
          
            isEdited.value.edited
              ? updateTask(isEdited.value.value, newTaskName.value, newTaskDesc.value)
              : createTask(
                newTaskName.value,
                newTaskDesc.value,
                Math.ceil(Math.random() * 1000000)
              );
            resetInputs();
            taskModal.value = false;
          };
          
          const editModal = (task: SingleTask) => {
            newTaskName.value = task.name;
            newTaskDesc.value = task.description;
            isEdited.value = { edited: true, value: task.id };
          
            taskModal.value = true;
          };
          
          const getTaskList = () => {
            axios
              .get(`${API_URL.value}tasks`)
              .then((res) => (taskList.value = res.data));
          };
          
          const createTask = (name: string, description: string, id: number) => {
            const data = { name, description, id };
            axios.post(`${API_URL.value}task`, data).then(() => {
              getTaskList();
            });
          };
          
          const deleteTask = (id: number) => {
            axios.delete(`${API_URL.value}delete/${id}`).then(() => {
              getTaskList();
            });
          };
          
          const updateTask = (id: number, name: string, description: string) => {
            const data = { name, description };
            axios.put(`${API_URL.value}edit`, data).then(() => {
              getTaskList();
            });
          };
          
          onMounted(() => {
            API_URL.value = import.meta.env.VITE_API;
            getTaskList();
          });
        </script>
      
        
    

If you haven't done it already, run npm start in your terminal. The app should be fully functional and should work correctly with backend.


Optimization

If you want to further optimize your application please visit: