Skip to main content

A lightweight, high-performance asynchronous Python micro-framework for building modern microservices and web applications

Project description

MicroFW

A lightweight, high-performance asynchronous Python micro-framework for building modern microservices and web applications.

Python Version License

Features

  • 🚀 Async-First Architecture - Built on ASGI with native async/await support for maximum performance
  • 🔌 Microservices Ready - Built-in service registry and client with automatic trace propagation
  • 🛡️ Robust Middleware System - Extensible middleware architecture with abstract base class
  • 💾 Database Integration - Seamless SQLAlchemy async ORM support with transaction management
  • ⚡ Concurrency Control - Automatic request limiting to prevent service overload
  • 📝 Data Validation - Pydantic integration for automatic request/response validation
  • 🎯 Dependency Injection - Automatic parameter injection for requests, database sessions, and validated models
  • ⚙️ Centralized Configuration - Environment-based settings management
  • 🔄 Lifecycle Events - Startup and shutdown hooks for resource management

Installation

pip install microfw

Quick Start

from microfw.app import App
from microfw.response import Response
from microfw.asgi import ASGI

app = App()

@app.route("/", methods=["GET"])
async def index(request):
    return Response({"message": "Hello, MicroFW!"})

asgi = ASGI(app)

Run with:

uvicorn main:asgi --reload

Configuration

Configure MicroFW using environment variables:

Variable Default Description
MICROFW_SERVICE_NAME microfw-app Service name for distributed tracing
MICROFW_CONCURRENCY_LIMIT 100 Maximum concurrent requests
MICROFW_CONCURRENCY_MAX_WAIT 0.1 Maximum wait time (seconds) for request slot
MICROFW_DEBUG False Enable debug mode

Core Concepts

Routing

Define routes with path parameters and HTTP methods:

@app.route("/users/{user_id}", methods=["GET"])
async def get_user(request, user_id):
    return Response({"user_id": user_id})

Request Validation with Pydantic

Automatic request body validation and parsing:

from pydantic import BaseModel

class UserCreate(BaseModel):
    name: str
    email: str

@app.route("/users", methods=["POST"])
async def create_user(user_data: UserCreate):
    return Response({"name": user_data.name, "email": user_data.email}, status_code=201)

Database Integration

Built-in SQLAlchemy async support with automatic session management:

from microfw.orm_db import Database
from microfw.middleware.db import DatabaseMiddleware
from microfw.middleware.transaction import TransactionMiddleware
from microfw.model import Base
from sqlalchemy import String
from sqlalchemy.orm import Mapped, mapped_column

# Initialize database
db = Database("sqlite+aiosqlite:///app.db")
app.middleware(DatabaseMiddleware(db))
app.middleware(TransactionMiddleware())

# Define models
class User(Base):
    __tablename__ = "users"
    id: Mapped[int] = mapped_column(primary_key=True)
    name: Mapped[str] = mapped_column(String(50))

# Lifecycle hooks
@app.on_event("startup")
async def startup():
    await db.connect()
    async with db.engine.begin() as conn:
        await conn.run_sync(Base.metadata.create_all)

@app.on_event("shutdown")
async def shutdown():
    await db.disconnect()

# Use in routes
@app.route("/users", methods=["GET"])
async def list_users(request):
    from sqlalchemy import select
    result = await request.db.execute(select(User))
    users = result.scalars().all()
    return Response([{"id": u.id, "name": u.name} for u in users])

Microservices Communication

Service-to-service communication with automatic trace propagation:

# Register services
app.add_service("inventory", "http://inventory-service:8001")
app.add_service("payment", "http://payment-service:8002")

# Call services
@app.route("/order", methods=["POST"])
async def create_order(request):
    # Trace IDs are automatically propagated
    inventory_response = await request.client.get("inventory", "/items/123")
    
    if inventory_response.status_code == 200:
        payment_response = await request.client.post(
            "payment", 
            "/charge",
            json={"amount": 100}
        )
        return Response(payment_response.json())
    
    return Response({"error": "Item not found"}, status_code=404)

Middleware

Create custom middleware by extending the base class:

from microfw.middleware import Middleware
from microfw.response import Response

class AuthMiddleware(Middleware):
    async def __call__(self, request, call_next):
        token = request.headers.get("Authorization")
        if not token:
            return Response({"error": "Unauthorized"}, status_code=401)
        
        # Validate token logic here
        response = await call_next(request)
        return response

# Apply globally
app.middleware(AuthMiddleware())

# Or per-route
@app.route("/protected", middlewares=[AuthMiddleware()])
async def protected_route(request):
    return Response({"message": "Access granted"})

Concurrency Control

Protect your service from overload with built-in concurrency limiting:

from microfw.middleware.concurrency import ConcurrencyMiddleware

# Global limit
app.middleware(ConcurrencyMiddleware(limit=100, max_wait=0.1))

# Per-route limit
@app.route("/heavy-task", middlewares=[ConcurrencyMiddleware(limit=5)])
async def heavy_task(request):
    # Only 5 concurrent requests allowed
    return Response({"status": "processing"})

Transaction Management

Automatic database transaction handling:

from microfw.middleware.transaction import TransactionMiddleware

# Automatic commit on success, rollback on error
@app.route("/users", methods=["POST"], middlewares=[TransactionMiddleware()])
async def create_user(request):
    data = await request.json()
    user = User(name=data["name"])
    request.db.add(user)
    # Transaction automatically committed
    return Response({"status": "created"}, status_code=201)

Advanced Features

Dependency Injection

MicroFW automatically injects dependencies based on parameter names and type hints:

  • request - The request object
  • request.db - Database session (when DatabaseMiddleware is enabled)
  • request.client - Service client for microservices communication
  • Pydantic models - Automatic request body validation

Lifecycle Events

Hook into application lifecycle:

@app.on_event("startup")
async def on_startup():
    print("Application starting...")
    # Initialize resources

@app.on_event("shutdown")
async def on_shutdown():
    print("Application shutting down...")
    # Cleanup resources

Context Propagation

Automatic trace ID and span propagation for distributed tracing:

# Access trace information
@app.route("/trace-info")
async def trace_info(request):
    return Response({
        "trace_id": request.context.trace_id,
        "span_id": request.context.span_id
    })

Examples

Complete CRUD Application

from microfw.app import App
from microfw.response import Response
from microfw.asgi import ASGI
from microfw.orm_db import Database
from microfw.middleware.db import DatabaseMiddleware
from microfw.middleware.transaction import TransactionMiddleware
from microfw.model import Base
from sqlalchemy import String, select
from sqlalchemy.orm import Mapped, mapped_column
from pydantic import BaseModel

app = App()
db = Database("sqlite+aiosqlite:///app.db")

# Middleware
app.middleware(DatabaseMiddleware(db))
app.middleware(TransactionMiddleware())

# Model
class Item(Base):
    __tablename__ = "items"
    id: Mapped[int] = mapped_column(primary_key=True)
    name: Mapped[str] = mapped_column(String(50))

# Validation Schema
class ItemCreate(BaseModel):
    name: str

# Lifecycle
@app.on_event("startup")
async def startup():
    await db.connect()
    async with db.engine.begin() as conn:
        await conn.run_sync(Base.metadata.create_all)

@app.on_event("shutdown")
async def shutdown():
    await db.disconnect()

# Routes
@app.route("/items", methods=["GET"])
async def list_items(request):
    result = await request.db.execute(select(Item))
    items = result.scalars().all()
    return Response([{"id": i.id, "name": i.name} for i in items])

@app.route("/items", methods=["POST"])
async def create_item(request, item_data: ItemCreate):
    item = Item(name=item_data.name)
    request.db.add(item)
    await request.db.commit()
    await request.db.refresh(item)
    return Response({"id": item.id, "name": item.name}, status_code=201)

@app.route("/items/{id}", methods=["GET"])
async def get_item(request, id):
    result = await request.db.execute(select(Item).where(Item.id == int(id)))
    item = result.scalar_one_or_none()
    if item:
        return Response({"id": item.id, "name": item.name})
    return Response({"error": "Not found"}, status_code=404)

asgi = ASGI(app)

Performance

MicroFW is designed for high-performance asynchronous request handling. Below are the results from Locust stress testing:

Load Test Results

Locust Statistics

Stress test statistics showing request throughput, response times, and failure rates

Requests Per Second

Total Requests Per Second

Real-time requests per second (RPS) during load testing, demonstrating the framework's ability to handle high concurrent loads

The framework successfully handles high concurrent loads with minimal latency, making it suitable for production microservices deployments.

Requirements

  • Python 3.7+
  • uvicorn (ASGI server)
  • sqlalchemy (Database ORM)
  • aiosqlite (Async SQLite driver)
  • pydantic (Data validation)
  • httpx (HTTP client for service calls)

License

MIT License - see LICENSE file for details.

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

Acknowledgments

This project was built with inspiration and knowledge from various excellent resources:

Special thanks to the open-source community for their continuous support and contributions.

Support

If you find this project helpful, consider supporting its development:

Buy Me A Coffee

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

microfw-0.1.0.tar.gz (216.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

microfw-0.1.0-py3-none-any.whl (17.4 kB view details)

Uploaded Python 3

File details

Details for the file microfw-0.1.0.tar.gz.

File metadata

  • Download URL: microfw-0.1.0.tar.gz
  • Upload date:
  • Size: 216.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.3

File hashes

Hashes for microfw-0.1.0.tar.gz
Algorithm Hash digest
SHA256 699dfa57bf62238aa52c89cb9a9a555825dd955b4dac08d4fb3df66d788570a0
MD5 626f8e34299900285af3868f4533ffa1
BLAKE2b-256 16a3dbec3ad3691bfb850943597f00432a3dd423d35cd9b2206ea87a37dbe7e4

See more details on using hashes here.

File details

Details for the file microfw-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: microfw-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 17.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.3

File hashes

Hashes for microfw-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 6b4aa5f9d87768d351f0c216f08a9f48736e74501d538aa47a822d0aa5d00a79
MD5 d01e1455be7a100c272b057d760388ea
BLAKE2b-256 da1212bbe45ca662123b12b3a34207cfd48e23924d3a1f02688257b7e34321f2

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page