Flask’s minimalism is a feature, not a limitation, but it means you’re responsible for every architectural decision, from database connections to route organization. This reference covers the core patterns that come up when building Flask APIs in production, from environment setup to MongoDB integration.
TL;DR: Set up a complete Flask API with routes, database connection via Peewee ORM, use cases, repositories, Swagger docs, and MongoDB integration.
Stack: Python, Flask, Flask-CORS, Peewee ORM, PostgreSQL, MongoDB, python-dotenv, Swagger
Level: Intermediate
Reading time: ~12 min
Flask is the right tool when you need a lightweight API without Django’s overhead, particularly useful for microservices and AI inference endpoints where control over the stack matters more than convention.
Install Flask and CORS
pip install flask
pip install flask-cors
pip install python-dotenv
First route
from flask import Flask
from flask_cors import CORS
app = Flask(__name__)
CORS(app)
@app.route("/")
def hello():
return "Hello with cors"
Install Peewee and drivers
pip install peewee
pip install psycopg
pip install psycopg-binary
Singleton connection (infra/repositories/singleton_connection.py)
A singleton ensures your app opens exactly one database connection per process lifecycle, critical in production to avoid connection pool exhaustion.
from peewee import PostgresqlDatabase
import os
from dotenv import load_dotenv
load_dotenv()
db = PostgresqlDatabase(
os.getenv('DB_NAME', 'djangopoc'),
user=os.getenv('DB_USER', 'admin'),
password=os.getenv('DB_PASSWORD', '123456'),
host=os.getenv('DB_HOST', 'localhost'),
port=int(os.getenv('DB_PORT', 5432))
)
class SingletonConnection:
_instance = None
def __new__(cls):
if cls._instance is None:
cls._instance = super().__new__(cls)
cls._instance._connection = db
try:
cls._instance._connection.connect()
print("Database connection successful!")
except Exception as e:
print(f"Database connection failed: {e}")
return cls._instance
def get_connection(self):
return self._connection
singleton_connection = SingletonConnection()
Configure routes
from flask import jsonify
from application.use_cases.list_positions.list_positions import ListPositions
from infra.repositories.position_repository_postgre import PositionRepositoryPostgre
def positions_route(app):
@app.route("/list-positions", methods=['GET'])
def list_positions():
try:
use_case = ListPositions(PositionRepositoryPostgre())
positions = use_case.execute()
result_list = [{'name': p.name, 'id': p.id} for p in positions.items]
return jsonify(result_list), 200
except Exception as e:
return jsonify({'error': str(e)}), 500
Use cases, repositories, and tests
The architecture follows the same pattern throughout: the route handles HTTP, the use case handles business logic, and the repository abstracts the database. Each layer is testable in isolation. Swap the concrete repository implementation without touching your use cases, and mock the repository to test use case logic without a real database connection.
Swagger
pip install flasgger
# setup in app.py
from flasgger import Swagger
swagger = Swagger(app)
MongoDB
pip install pymongo
Same singleton pattern as PostgreSQL, just swap the driver. The MongoDB repository uses the aggregation pipeline for grouped queries, which replaces GROUP BY from SQL.
What you’ve built
A complete Flask reference covering the full stack: environment setup, database integration with Peewee ORM, clean architecture with use cases and repository protocols, test patterns, Swagger docs, and MongoDB connectivity. This structure scales from a side project to a production API without a rewrite.
Next steps
- Add authentication: integrate Flask-JWT-Extended or an API key middleware to secure your endpoints.
- Containerize it: wrap this setup in a Dockerfile and connect to a CI/CD pipeline.
- Add observability: plug in structured logging and AWS X-Ray tracing to get visibility into production requests.
Questions or feedback? Find me on LinkedIn or GitHub.