Flask + VueJS + MongoDB: Simple CRUD Application — Part I

Saurav Samantray
4 min readJan 19, 2021

What will we learn?

How to create a simple application in VueJS that will use flask as backend to demonstrate CRUD operations on a User model. In Part I of this series will cover topics related to creation of a flask backend application that connects with mongoDB and exposes CRUD operations as REST APIs.

Part I: Expose CRUD operations on User model as REST API using Flask.

Part II: User-friendly UI using Vue.js to talk to Flask backend.

Architecture

Code Structure

flask-vuejs-mongodb-crud│ .env
│ .gitignore
│ docker-compose-dev.yml
│ docker-compose.yml
│ README.md

├───nginx
│ Dockerfile
│ nginx.conf

├───client

├───server
│ app.py
│ config.py
│ errors.py
│ requirements.txt
│ Dockerfile

├───────db
│ models.py
│ __init__.py

└───────rest
routes.py
user.py
__init__.py

The folder named server under project root (flask-vuejs-mongodb-crud) holds the flask backend application and related files.

Configuration

MongoDB related values are set to variables prefixed with MONGODB_. The values are fetched from environment variables set in .env file.

Database(MongoDB) models and connection initialization

flask-mongoengine extension has been used for creating the integration between flask and mongo. The initialize_db() method will be later imported in app.py and used for initializing the DB connection on server startup. Logger has been added to the method to provide confirmation on server startup.

models.py inside db folders define a simple User model that has a name and email (defined to be unique to avoid multiple users with same email). User extends MongoEngine’s Document class to enable basic ORM type operation but on document database like mongoDB.

Restful API Resources

flask_restful extension has been used to create clean REST APIs that adhere to best practices and works well with ORM extensions

UsersApi resource will serve requests for fetching all available users using the get() method and create a single user using the post() method.

UserApi resource will server request for update user via put(), delete user via delete() and fetch a single user via get().

Custom error handling

We have placed custom error handling by implementing try/except on possible errors and raising custom errors with relevant messages for the client. For example, if the client tries to create a user with an existing email, MongoEngine will throw NotUniqueError (email field in User model has been defined to be unique). But this error doesn’t make up for a meaningful message and http status code for client. We have intercepted the exception in except block and raised a custom error that defines appropriate message and http status code.

#errors.py
from werkzeug.exceptions import HTTPException
class InternalServerError(HTTPException):
pass
class SchemaValidationError(HTTPException):
pass
class UserNotFoundError(HTTPException):
pass
class EmailAlreadyExistError(HTTPException):
pass
errors = {
"InternalServerError": {
"message": "Oops something wrong",
"status": 500
},
"SchemaValidationError": {
"message": "Required fields missing",
"status": 400
},
"UserNotFoundError": {
"message": "User not found in database",
"status": 400
},
"EmailAlreadyExistError": {
"message": "User with specified email already exists in database",
"status": 400
},
}

All custom error classes should extend HTTPexception class. The messages and status code are defined in errors dictionary which will be passed on to Api initializer later in app.py.

Routing requests

We learned how the requests are served and errors are handled, but how are the requests routed to these resources? That is done using the routes.py file in folder named rest.

#rest/routes.py
from .user import UsersApi, UserApi
def initialize_routes(api):
api.add_resource(UsersApi, '/users')
api.add_resource(UserApi, '/users/<id>')

With rest resources defined in user.py and routing handled by routes.py, we need to define an initialization method that can be later called by app.py to initialize the rest endpoints with proper configuration. This will be done in __init__.py in rest folder.

#rest/__init__.py
from flask_restful import Api
from .routes import initialize_routes
from errors import errorsapi = Nonedef initialize_api(app):
app.logger.info("Initializing REST Apis")
api = Api(app, errors=errors)
initialize_routes(api)

As in case of db initialization, logger has been added to ensure confirmation of the intialization.

Stitching all together in app.py!

We have kept the app.py simple by moving all extension (MongEngine and flask-restful) related code to respective modules and linked them to app.py via initialize_*() methods.

#app.py
import json
from flask import Flaskfrom config import BaseConfig
from db import initialize_db
from rest import initialize_api
app = Flask(__name__)
app.config.from_object(BaseConfig)
initialize_db(app)initialize_api(app)if __name__ == '__main__':
app.run(debug=True,host='0.0.0.0',port=8000)

Starting servers in development mode

As with my previous articles I have created a dev docker compose that will not only spin up mongoDB instance, but also run the flask application in development mode with hot reload!

Go to the root of the project (flask-vuejs-mongodb-crud) and execute

docker-compose -f docker-compose-dev.yml up –build

You should see the DB and REST initialization entries in logs along with others.

fms_dev_server | [2021-01-19 04:56:26,315] INFO in __init__: Initializing MongoDB
fms_dev_server | [2021-01-19 04:56:26,347] INFO in __init__: Initializing REST Apis

Time for testing

Create user

curl --location --request POST 'localhost:8000/users' \--header 'Content-Type: application/json' \--data-raw '{"name": "laura","email": "laura1@gmail.com"}'

Fetch all users

curl --location --request GET 'localhost:8000/users'

Go on, play around with other CRUD operation like update and delete :).

In part II of this series we shall create a client application in VueJS which will perform all CRUD operations on User model and handle error response with a appealing UI.

Github link of complete source code here.

Part II here.

Happy Coding!

--

--