Best Flask open-source libraries and packages

Online store rest api

An example online store REST API using flask, SQL ORM and OpenAPI Specifications (with WIP Vue.js frontend)
Updated 1 year ago

Online Store Example

   GitHub pull-requests closed Python CI Frontend Docker CI/CD Backend Docker CI/CD Sphinx Documentation CI

Components 📲🧩

There is a Vue.js based front-end for displaying reactive web pages to the user and a Python Flask backend for serving the online store REST API.

Running The Node.JS Frontend 🏃‍♀️☕📜

The frontend can be started with the following command:

$ npm run frontend:start  # starts Vue.js app at http://localhost:8080

Note that the frontend can be found under online_store/frontend (WIP).

Running The Python Backend 🏃‍♂️🐍🔚

The backend can be started with the following command:

$ export FLASK_DEBUG=1  # for development with live reload
$ export FLASK_ENVIRONMENT=development  # this is the default
$ PYTHONPATH='.' python3 manage.py run

Background 📖

This repository was created to solve the Prezola Technical Challenge, which requires a solution capable of adding, removing and listing (added) gifts from a list. It also required a mechanism to purchase a gift from the list and generate a report (of purchased vs non-purchased) gifts.

This code project takes that concept and extends it to a generic (gift) store.

The challenge

"Write a program to the best of your knowledge which will allow the user to manage a single list of wedding gifts."

The user must be able to:

  • Add a gift to the list
  • Remove a gift from the list
  • List the already added gifts of the list
  • Purchase a gift from the list
  • Generate a report from the list which will print out gifts & their statuses.
    • The report must include two sections:
      • Purchased gifts: each purchased gift with their details.
      • Not purchased gifts: each available gift with their details.

Implementation Notes 📄

There are two concrete implementations for realising a gift list with the following classes from online_store/backend/gift_list.py:

  • BasicGiftList, a pure python implementation of a gift list (Well Tested).
  • SqlDatabaseGiftList, an SQL ORM based implementation of a gift list for use within a flask (or Django) REST API app. In this example, the ORM models are found in online_store/backend/models/ and the REST API is implemented in online_store/backend/routes/gifts.py.

Development Setup ⚙️

$ npm install  # needed for Swagger JSON to OAS YAML spec conversion
$ npm run frontend:install  # install packages needed for frontend app
$ python3 -m venv venv
$ source venv/bin/activate
(venv) $ pip install setuptools
(venv) $ pip install -r requirements-dev.txt -r requirements-test.txt
(venv) $ pip install -r requirements.txt

Testing 🧪

setup.cfg has configured pytest to collect coverage information and can be run as follows:

(venv) $ PYTHONPATH='.' py.test 

Basic Gift List Implementation 🎁

The following showcases a simple python implementation of the gift list:

# load store data
>>> import json
>>> store_items = json.load(open('products.json'))
# import implementation
>>> from online_store.backend.gift_list import BasicGiftList
# create a new gift list
>>> gift_list = BasicGiftList('Liam')
>>> gift_list  # show list representation in interpreter
Liam -> []
# add gift item
>>> gift_list.add_item(store_items[0])
>>> gift_list
Liam -> [{'id': 1, 'name': 'Tea pot', 'brand': 'Le Creuset', 'price': '47.00GBP', 'in_stock_quantity': 50}]
# remove gift item
>>> gift_list.add_item(store_items[1], quantity=3)
>>> gift_list.remove_item(store_items[0])
>>> gift_list
Liam -> [{'id': 2, 'name': 'Cast Iron Oval Casserole - 25cm; Volcanic', 'brand': 'Le Creuset', 'price': '210.00GBP', 'in_stock_quantity': 27}]
# purchase gift item
>>> gift_list.purchase_item(store_items[1], quantity=1)
# generate report
>>> gift_list.create_report()
Gift List Report for Liam:
==============================
Purchased items:
   - {'id': 2, 'name': 'Cast Iron Oval Casserole - 25cm; Volcanic', 'brand': 'Le Creuset', 'price': '210.00GBP', 'in_stock_quantity': 27} (quantity: 1)
------------------------------
Available items:
  - {'id': 2, 'name': 'Cast Iron Oval Casserole - 25cm; Volcanic', 'brand': 'Le Creuset', 'price': '210.00GBP', 'in_stock_quantity': 27} (quantity: 2)

Flask REST API + SQL ORM Implementation 🌍🕸️

Alternatively there is a REST API, which can be run with:

$ source venv/bin/activate
(venv) $ cd online_store
(venv) $ FLASK_DEBUG=1 flask run

This will start a flask development server running on http://localhost:5000 and provides a Swagger-UI at http://localhost:5000/apidocs/.

Examples

# register a new user
$ curl -X POST -H "Content-Type: application/json" -d '{"username": "me", "password": "test", "email": "me@test.com"}' 'http://localhost:5000/api/v1/auth/register'

{"code":200,"msg":"success","status":"ok"}

# login with user
$ curl -X POST -H "Content-Type: application/json" -d '{"username": "me", "password": "test"}'

{"access_token":"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE2MDIwMTIxMzQsIm5iZiI6MTYwMjAxMjEzNCwianRpIjoiOTA5NTRlZDAtZWIzMy00MTY2LThhODEtZDE5NDI3MjI5NTE0IiwiZXhwIjoxNjAyMDEzMDM0LCJpZGVudGl0eSI6Im1lIiwiZnJlc2giOmZhbHNlLCJ0eXBlIjoiYWNjZXNzIn0.GYVTQK4Xw9JaiJJxa75vlKBS-mho0QjfcM94usPZtSI","code":200,"status":"ok"}

# note access token
$ export TOKEN='<JWT_FROM_LOGIN>'

# access gift list
$ curl -X GET -H  "accept: application/json" -H  "Authorization: Bearer $TOKEN" http://localhost:5000/api/v1/gifts/list

[]

# add one gift item with store id of 1 to list
$ curl -X POST -H  "accept: application/json" -H  "Authorization: Bearer $TOKEN" 'http://localhost:5000/api/v1/gifts/list/add?item_id=1&quantity=1'

{
  "code": 200,
  "msg": "Item added",
  "status": "ok"
}

# remove item with id 1 from gift list
$ curl -X DELETE -H  "accept: application/json" -H  "Authorization: Bearer $TOKEN" http://localhost:5000/api/v1/gifts/list/1

{
  "code": 200,
  "msg": "Item removed",
  "status": "ok"
}

# add then purchase item
$ curl -X POST -H  "accept: application/json" -H  "Authorization: Bearer $TOKEN" 'http://localhost:5000/api/v1/gifts/list/add?item_id=2&quantity=2'

{
  "code": 200,
  "msg": "Item added",
  "status": "ok"
}

$ curl -X POST -H  "accept: application/json" -H  "Authorization: Bearer $TOKEN" http://localhost:5000/api/v1/gifts/list/2/purchase?quantity=1

{
  "code": 200, 
  "msg": "gift purchased", 
  "status": "ok"
}

# produce report
$ curl -X GET -H  "accept: application/json" -H  "Authorization: Bearer $TOKEN" 'http://localhost:5000/api/v1/gifts/list/report'

{
  "available": [
    {
      "brand": "Le Creuset", 
      "currency": "GBP", 
      "id": 2, 
      "in_stock_quantity": 27, 
      "name": "Cast Iron Oval Casserole - 25cm; Volcanic", 
      "price": 210.0, 
      "quantity": 1
    }
  ], 
  "purchased": [
    {
      "brand": "Le Creuset", 
      "currency": "GBP", 
      "id": 2, 
      "in_stock_quantity": 27, 
      "name": "Cast Iron Oval Casserole - 25cm; Volcanic", 
      "price": 210.0, 
      "quantity": 1
    }
  ], 
  "user": 2
}

Bonus Features ✨

There are currently a number of extra features, which help

  • User-friendly backend application logging using loguru python package.
  • Simple containerisation using Docker - see DockerFile
  • Authentication using JSON web tokens via flask-jwt-extended middleware.
  • OpenAPI Specification (OAS) conformant client documentation generated using flasgger and viewable via SwaggerUI /apidocs endpoint when running the flask server.
  • Persistent data storage using SQL Database modelled using sqlalchemy ORM.

Developer Documentation 📗

The Sphinx documentation builder is currently used to extract python docstrings and the OpenAPI spec of the REST API.

To build the documentation:

$ cd docs/
$ make openapi_spec.yml
$ make html  # or another end documentation format e.g. epub

A live deployment to GitHub Pages can be found at https://liam-deacon.github.io/online-store-rest-api/

TODO 📝

  • [x] Build script / CI using GitHub Actions
  • [x] Sphinx documentation support
  • [x] Deploy API documentation to GitHub Pages via Actions
  • [ ] Test SqlDatabaseGiftList
  • [ ] Automated tests for REST API
  • [x] shields.io support for README badges
  • [ ] Add linting to CI
  • [x] Build and deploy docker image(s) to dockerhub via CI/CD
  • [x] Deploy flask app to Heroku for demo purposes (follow link)

Future Improvements 🔮

Given more time, the following improvements could be made:

  • [ ] Write frontend in React (or maybe Vue.js)
  • [ ] Create docker-compose.yml multi-container Docker compose script for orchestrating frontend, backend and database (e.g. React/NPM-based, Flask, Postgres).
  • [ ] Implement missing features in the code (i.e. wherever NotImplementError is raised)
  • [ ] Code tidy and refactor
  • [ ] Increase overall code coverage (aiming for nirvana at 100%)