Earlier this year I blogged about creating a Lean Mean Vue Machine called Quotes on Demand. The application was a fully featured CRUD application served from a NodeJS server and had a self contained VueJS front end.

But wouldn’t it be a nice test to see if that same Vue application could switch over to another API, say something like a Python web server powered by Flask?

In this post, we will create a Python web application that will have 100% parity to an existing NodeJS web application. This will enable an existing VueJS front end to connect to the application with no additional code changes in the user interface code.

What is Flask?

Flask is a micro web framework written in Python much like Express is for a Node web application. Flask supports HTTP verbs, routing, and handling the requests from the HTTP pipeline. With Flask being voted the most popular framework in the Python Developers Survey 2018 with a 47% usage, I decided to give it a spin.

Application Goals

This application was created with a couple of goals in mind.

Create a Python webserver which has 100% parity of the original NodeJS server. All CRUD functionality would be the same, using the same JSON data file as the original. Enable the VueJS application from Quotes on Demand to “just work” with the Python webserver. As in all front end functionality would be exactly the same with no changes to the existing application logic. Continue to have the VueJS application stand alone outside of the Python webserver.

Structure of the Application

The application structure is actually quite simple; it’s broken into two distinct sections: the UI and the API. Since we are building a new API for this application, we will focus on that section. For more information about the UI, please reference the Lean Mean Vue Machine blog entry.

The API folder is a fully self-contained Python application server that will load a selection of quotes up into an in-memory collection that will allow connecting parties to do the five main CRUD actives such as:

1 /quote | GET | returns all quotes 2 /quote/{id} | GET | returns a specific quote by 'id' 3 /quote | POST | adds a new quote 4 /quote/{id} | PUT | updates a quote 5 /quote/{id} | DELETE | deletes a quote

This server is CORS enabled, so all endpoints are open to public consumption and there is no security on the endpoints for simplicity. The quotes API itself does support proper HTTP verb endpoints.

How the API application was built?

I installed Flask and CORS using pip , the Python package manager, using these commands:

pip install -U flask

pip install -U flask-cors

The first file created was named app.py . Notice that all of the Python files are suffixed with the dot py extension to designate them as a Python file.

api/app.py

from flask import Flask, request from flask_cors import CORS import crud as crud app = Flask(__name__) CORS(app) @app.route('/') def health_check(): return 'The server is working.' @app.route('/quote/', methods=['GET']) def get_quotes(): return crud.get() @app.route('/quote/<id>', methods=['GET']) def get_quote(id): return crud.get_by_id(id) @app.route('/quote/', methods=['POST']) def create_quote(): if request.method == 'POST': quote = request.get_json() return crud.create(quote) @app.route('/quote/', methods=['PUT']) def update_quote(): if request.method == 'PUT': quote = request.get_json() return crud.update(quote) @app.route('/quote/<id>', methods=['DELETE']) def delete_quote(id): if request.method == 'DELETE': return crud.delete(id) if __name__ == '__main__': # app.run() app.run(debug=True, port=3010)

Code Explanations

From top to bottom, I’ll explain the code.

First, we’ve imported Flask and CORS from our included dependencies. Then we’ve imported a second Python file which was created to hold the actual CRUD functionality

We then create our Flask application and we tell CORS that it will be used within the application.

Six routes have been created, one each for a GET all function, a GET BY ID function, a POST for creating a new quote, a PUT for updating a quote and a delete for removing a quote from the collection. A root route function has been created called health check just to notify the calling applications that the API is up and running.

Notice in our five CRUD routes that we’re typically checking the request type from the endpoint. We’re also using the request.get_json() function from Flask to take a posted JSON object and placing it into a local variable for later usage. And last we’re typically just returning a function from our imported crud.py file from earlier.

At the bottom of the file, we’re checking to see if the application’s name is main and then we’re starting up the server.

Let’s take a look at the functionality held within crud.py .

api/crud.py

import json with open('data.json') as json_file: data = json.load(json_file) def get(): return json.dumps(data) def get_by_id(id): for item in data: if item['id'] == int(id): print('The object found is: ', item) return json.dumps(item) def create(quote): max_item = max(data, key=lambda ev: ev['id']) new_id = int(max_item['id']) + 1 print('The next ID is: ', new_id) quote['id'] = new_id print('The new quote is: ', quote) data.append(quote) return json.dumps(data) def update(quote): for index, item in enumerate(data): if item['id'] == int(quote['id']): print('The object was: ', item) item = quote print('The object now is: ', item) data[index] = item return json.dumps(data) def delete(id): for item in data: if item['id'] == int(id): data.remove(item) return json.dumps(data)

The crud.py file is pretty straight forward. We’re going to have five functions for each of the CRUD functions that exist in the app.py file.

Code Explanations

Once again, from top to bottom, I’ll explain the code.

We’re going to import the JSON utilities from the Python libraries so that we can work with our JSON data collection. Then we’re going to open that JSON file and load it into a local variable called data . This will be our in-memory database for everything we’re doing later.

Our first function is for our get all endpoint and we’re just going to do a simple dump of all of the data and send it back as JSON. The next function is our get by id function and here we’re looking for an id to be passed in from the endpoint. We’re casting that variable into an integer and then comparing that to all found id s within the data collection. If one is found, we’re sending that one back to the endpoint as JSON.

Our third function is to create a new quote.

The Python max() function is being used to find the highest id from the objects within the data collection. One thing to note here is the key=lambda usage. This is an anonymous function placed inline within the max function which will evaluate the actual key id from each object within the JSON collection.

Immediately after that, we’re using the returned max value to increment that key one step up and assigning that to the newly created quote.

The newly-created quote passed in on the function is then appended to the data collection and we return the data as JSON to the endpoint.

The fourth function is used for updating an item in the data collection. We’re going to do a for loop here with two variables. The first one is used for the actual indexier of the loop, and the second one is the actual item in the loop. We use Python’s enumerate function and turn our data into a collection that can be iterated upon.

Now we check the id of each item in the collection against the quote’s id . If they match, we’ll overwrite the existing data with the new data. Now we use the index variable and insert the updated item back into the data collection at the same point. After that, we return the data collection back to the endpoint as JSON.

Our last function in this file is for deleting an item. The function is passing in an id , we’re going to loop over the data collection and check each item’s id against the passed-in one. If one matches, we’re using the remove() function on the collection and delete that item. Once that’s done, we return the modified collection back to the endpoint as JSON.

Last we have the actual JSON file.

quotes/data.json

{ "id": 5, "author": "Stephen King", "text": "Get busy living or get busy dying." },

This is just a small snippet, but all of the rows followed in this same fashion by having an ID , an AUTHOR , and a TEXT property.

Python Conclusion

And that’s all we need for a fully functional CRUD API built in Python.

The goal of this project was to build a Python API with parity that matched with the original Quotes on Demand Node server. With just a little research on finding ways to iterate over the data and finding out how to modify those collections, I think we succeeded in our mission. Python is a simple but powerful language and this simple project shows great potential for expansion.

For more information about the VueJS user interface used here, see the Lean Mean Vue Machine blog found on the Keyhole Software blog.