Single Page Apps with Vue.js and Flask: AJAX Integration

AJAX Integration with REST API

Thanks for joining me for the fifth post on using Vue.js and Flask for full-stack web development. This post will be fairly short, but highly valuable as I will be demonstrating how to connect the front-end and back-end applications using Asynchronous Javascript and XML (aka, AJAX).

The code for this post can be found on my GitHub account under the branch FifthPost.

Series Content

  1. Seup and Getting to Know VueJS
  2. Navigating Vue Router
  3. State Management with Vuex
  4. RESTful API with Flask
  5. AJAX Integration with REST API (you are here)
  6. JWT Authentication
  7. Deployment to a Virtual Private Server

Brief Explanation of AJAX and Thick Client Apps

AJAX is a powerful technology that has had enormous success in building highly interactive and fast web applications. In fact, I believe AJAX is probably the most impactful technology that has fueled a major paradigm shift from the era of thin client web apps to the increasingly thick or fat client web apps we see today. The survey application that I have been demonstrating in this series is considered a distributed client server application implemented as a thick client. This means that the majority of the logic and behavior is implemented on the client via JavaScript (mainly using Vue.js) while the server side is a fairly dumb RESTful API that simply consumes and serves up data.

There are two major technologies that enable this thick client application to juggle the maintenance of state (data), behavior, and data driven presentation.

  1. The Vue.js framework with its excellent reactivity system shared along with the vuex flux-like library
  2. AJAX functionality implemented within a well designed JavaScript library called axios

You should already be familiar with the awesomeness afforded by Vue.js and vuex from my prior posts in this series. So I will focus on explaining AJAX in this article. AJAX is a network communication technology that uses HTTP to push and pull data to and from a server in an asynchronous manner within the browser. In this way the browser is able to rapidly update individual components that are dependent on small amounts of data, rather than updating the entire page, which leads to a far more responsive experience.

Setting up Axios and Enabling CORS

To install axios I will use npm and save it to my the package.json file with the following command:

$ npm install --save axios

Now, in order to use axios to make AJAX requests from the client to the back-end server I will need to make a change to the Flask application to enable Cross Origin Resource Sharing (CORS). Anytime a client makes a request for a resource that resides on another machine as defined by protocol, IP address / domain name, or port number then additional headers associated with CORS must be added. Luckily, the there is a handy little Flask extension, Flask-CORS, that makes integration with Flask very easy. As done previously I use pip to install it like so.

(venv)$ pip install Flask-CORS

Next I need to import and instantiate the CORS extension object then register it with the Flask application object within the application.py module of the back-end application.

"""
application.py  
- creates a Flask app instance and registers the database object
"""

from flask import Flask  
from flask_cors import CORS

def create_app(app_name='SURVEY_API'):  
  app = Flask(app_name)
  app.config.from_object('surveyapi.config.BaseConfig')

  cors = CORS(app, resources={r"/api/*": {"origins": "*"}})

  from surveyapi.api import api
  app.register_blueprint(api, url_prefix="/api")

  from surveyapi.models import db
  db.init_app(app)

  return app

Implementing AJAX Requests with Axios

Next up I need to replace the mock functions I implemented previously in scr/api/index.js with GET, POST, and PUT requests using the axios library. If you remember from the last article I defined the following RESTful API endpoints in the Flask application which will need to be called from JavaScript within the browser.

Route Method Functionaility
/api/surveys/ GET Retrieve all surveys
/api/surveys/ POST Create a new survey
/api/surveys/id/ GET Retrieve a survey by id
/api/surveys/id/ PUT Update a survey's choice selections

In src/api/index.js I need to import the axios library and for reusability I also define a variable called API_URL that is equal to the root of the API resource http://127.0.0.1:5000/api. Then I replace the body of the existing functions to use the axios methods get(...), put(...), and post(...) like so:

// api/index.js

import axios from 'axios'

const API_URL = 'http://127.0.0.1:5000/api'

export function fetchSurveys() {  
  return axios.get(`${API_URL}/surveys/`)
}

export function fetchSurvey(surveyId) {  
  return axios.get(`${API_URL}/surveys/${surveyId}/`)
}

export function saveSurveyResponse(surveyResponse) {  
  return axios.put(`${API_URL}/surveys/${surveyResponse.id}/`, surveyResponse)
}

export function postNewSurvey(survey) {  
  return axios.post(`${API_URL}/surveys/`, survey)
}

Now there are only two small changes to make in src/store/index.js to accommodate the convention used by axios to return data from get requests. In the loadSurvey(...) and loadSurveys(...) action methods, they are each calling the function that in turn calls the axios.get(...) methods, which return promises. When these promises resolve they will contain the data returned by the AJAX request to the server and that data will be held in a .data member of the resolved promise object. So, the data passed to the mutations will need to be response.data instead of just response.

const actions = {  
  // asynchronous operations
  loadSurveys(context) {
    return fetchSurveys()
      .then((response) => {
       // context.commit('setSurveys', { surveys: response })
        context.commit('setSurveys', { surveys: response.data })
      })
  },
  loadSurvey(context, { id }) {
    return fetchSurvey(id)
      .then((response) => {
        // context.commit('setSurvey', { survey: response })
        context.commit('setSurvey', { survey: response.data })
      })
  },

And voila! How simple is that?

Since all the heavy lifting was already taken care of building out the front-end and back-end in prior posts the work required to integrate AJAX communication is literally that simple. All there is left to do is just fire up the dev servers and take the survey application for a drive.

Conclusion

In this short article I have demonstrated how to connect the front-end and back-end application via the powerful AJAX communication technology. To accomplish this I utilized the axios library to handle the client side of things, and on the back-end I utilized the Flask-CORS extension to enable CORS.

Thank you for reading and please join me for the next article where I am going to cover implementing registering and authenticating users. As always I welcome any comments or criticism below.

Author image
Lincoln, Nebraska Twitter