Python Falcon Microservice With MongoDB and Docker
In this article, we look at how to create a simple Flask API with MongoDB, deploy, and run it inside a docker container.
Join the DZone community and get the full member experience.
Join For FreeIn this article, we are going to build a book API where the user can create a book, update the book, and fetch book by id.
Prerequisites
- Docker (1.6.0 or above)
- Docker-compose (1.26+)
- Python 3.5 or above
What Is Falcon Web Framework?
Falcon is a reliable, high-performance Python web framework for building large-scale app backends and microservices. It encourages the REST architectural style and tries to do as little as possible while remaining highly effective.
We are using docker-compose to easily set up a MongoDB instance with data persistent support.
xxxxxxxxxx
version: '3'
services:
mongo:
image: mongo
ports:
- "27017:27017"
volumes:
- "mongodata:/data/db"
environment:
MONGO_INITDB_ROOT_USERNAME: root
MONGO_INITDB_ROOT_PASSWORD: monngoexample
MONGO_INITDB_DATABASE: falconapidb
volumes:
mongodata:
$ docker-compose up -d
Set up Falcon
We’ll do is install Falcon inside a fresh virtualenv. To that end, let’s create a new project folder called falcon-book-api
, and set up a virtual environment.
x
$ mkdir falcon-book-api
$ cd falcon-book-api
$ virtualenv .venv
$ source .venv/bin/activate
$ pip install falcon
The project structure is:
x
├── facon-book-api
├── .venv
├── docker-compose.yml
├── Dockerfile
├── requirements.txt
├── src
│ ├── model
│ ├── app.py
│ ├── common
│ └── resource
├── test
└── deployment
For better organization, the architecture has been structured as follows
src/model
: mongodb's data modelssrc/common
: common data and methodssrc/resource
: inbound requests, validationdeployment
: deployment configuration files.test
: testing module
In models, we used MongoEngine ODM (Object Document Mapper). It offers a great deal of control & flexibility for working with MongoDB. Defining schemas for our documents can help iron out bugs involving incorrect types or missing fields, and also allows us to define utility methods on our documents in the same way as traditional ORMs.
xxxxxxxxxx
from mongoengine import *
class Book(Document):
title = StringField(max_length=200, required=True)
price = FloatField()
description = StringField()
author = StringField(max_length=50, required=True)
app.py
source code is a below.
xxxxxxxxxx
import falcon
import pathlib
from falcon_swagger_ui import register_swaggerui_app
import src.common.constants as constants
from src.common.cors import Cors
from src.common.json_translator import JSONTranslator
from src.common.require_json import RequireJSON
import mongoengine as mongo
from src.common.handlers import ExceptionHandler as handler
from src.resource.book_resource import BookResource
mongo.connect(
constants.MONGO['DATABASE'],
host=constants.MONGO['HOST'],
port=constants.MONGO['PORT'],
username=constants.MONGO['USERNAME'],
password=constants.MONGO['PASSWORD']
)
STATIC_PATH = pathlib.Path(__file__).parent / 'static'
app = falcon.API(middleware=[Cors(), RequireJSON()])
book = BookResource()
app.add_route('/api/book/', book)
app.add_route('/api/book/{book_id}', book, suffix="id")
app.add_static_route('/static', str(STATIC_PATH))
# global handler exception of application
app.add_error_handler(Exception, handler.handle_500)
# handler for not found resources
app.add_sink(handler.handle_404, '^((?!static).)*$')
register_swaggerui_app(app, constants.SWAGGERUI_URL, constants.SCHEMA_URL, page_title=constants.PAGE_TITLE,
favicon_url=constants.FAVICON_URL,
config={'supportedSubmitMethods': ['get', 'post', 'put', 'delete'], })
Mongo.connect(database, host=, port=, username=, password=), this line will automatically create a connection to the mongodb database with given credentials. The connection will be globally use throughout the scope of an app.
App = falcon.API(), this line will creates your WSGI application and aliases it as app
.
Book = BookResource(), create and instance of the resource.
App.add_route('/api/book/', book) and app.add_route('/api/book/{book_id}', book, suffix="id"), will route the http path and method to the respective methods of the resource.
We can now start our application. We are using the Gunicorn server.
In development mode, rungunicorn --reload src.app:app
(Note the use of the --reload
option to tell Gunicorn to reload the app whenever its code changes.)
Build and Run the Service Using Docker Compose
Supervisor is a system that allows to monitor and control the Gunicorn server.
deployment/gunicorn.conf
x
[supervisord]
nodaemon=true
[program:gunicorn_book]
command=gunicorn src.app:app --worker-class gevent -w 2 --bind 0.0.0.0:3000 --log-file=-
directory=/usr/api
stdout_logfile = /usr/api/logs/main.log
user=nobody
stdout_logfile_maxbytes=5MB
stdout_logfile_backups=50
stdout_capture_maxbytes=1MB
stdout_events_enabled=false
autostart=true
autorestart=true
redirect_stderr=true
loglevel=info
Full docker-compose file.
xxxxxxxxxx
version: '3'
services:
mongo:
image: mongo
ports:
- "27017:27017"
volumes:
- "mongodata:/data/db"
environment:
MONGO_INITDB_ROOT_USERNAME: root
MONGO_INITDB_ROOT_PASSWORD: mongoexample
MONGO_INITDB_DATABASE: falconapidb
book-api:
build:
context: ./
dockerfile: ./Dockerfile
ports:
- '3000:3000'
volumes:
- ./:/usr/api
- ./logs/:/usr/api/logs
volumes:
mongodata:
Run docker-compose up -d
x
POST /api/book HTTP/1.1
Host: 127.0.0.1:3000
Content-Type: application/json
{
"title": "The Code Book: The Secret History of Codes and Code-breaking",
"price": 100,
"description": "Book description",
"author": "Simon Singh"
}
The complete source code can be found in my GitHub repository.
Opinions expressed by DZone contributors are their own.
Trending
-
Extending Java APIs: Add Missing Features Without the Hassle
-
Comparing Cloud Hosting vs. Self Hosting
-
Competing Consumers With Spring Boot and Hazelcast
-
Effortlessly Streamlining Test-Driven Development and CI Testing for Kafka Developers
Comments