pyfastmvc 1.4.0
Production-grade MVC Framework for FastAPI - Generate projects, entities, and migrations with a single command
License: MIT
Requires Python: >=3.10
Keywords: fastapi, mvc, framework, api, rest, web, backend, python, scaffold, generator, crud, authentication, jwt, sqlalchemy, pydantic, alembic, migrations, redis, caching

<div align="center">

# 🚀 FastMVC

### Production-Ready MVC Framework for FastAPI

[![PyPI version](https://img.shields.io/pypi/v/pyfastmvc.svg?style=for-the-badge&logo=pypi&logoColor=white)](https://pypi.org/project/pyfastmvc/)
[![Python](https://img.shields.io/badge/Python-3.10+-3776ab.svg?style=for-the-badge&logo=python&logoColor=white)](https://www.python.org)
[![FastAPI](https://img.shields.io/badge/FastAPI-0.100+-009688.svg?style=for-the-badge&logo=fastapi&logoColor=white)](https://fastapi.tiangolo.com/)
[![License](https://img.shields.io/badge/License-MIT-yellow.svg?style=for-the-badge)](https://opensource.org/licenses/MIT)

**Build enterprise-grade APIs in minutes, not hours.**

[Installation](#-installation) •
[Quick Start](#-quick-start) •
[Features](#-features) •
[Documentation](#-documentation) •
[Contributing](#-contributing)

---

</div>

## 🎯 Why FastMVC?

| Pain Point | FastMVC Solution |
|------------|------------------|
| 🏗️ **Project Setup Takes Hours** | One command generates a complete project structure |
| 📁 **Inconsistent Code Organization** | Enforced MVC pattern with clear separation of concerns |
| 🔐 **Security is an Afterthought** | JWT, rate limiting, and security headers built-in |
| 🗄️ **Database Migrations are Complex** | Simple `fastmvc migrate` commands |
| ✏️ **Writing CRUD is Repetitive** | Auto-generate entities with full CRUD scaffolding |
| 🧪 **Testing Setup is Tedious** | Pre-configured pytest with fixtures included |
| 🛡️ **Middleware is Scattered** | [90+ production-ready middlewares](https://pypi.org/project/fastmvc-middleware/) included |

---

## 📦 Installation

### Using uv (Recommended)

[uv](https://docs.astral.sh/uv/) is a blazing fast Python package manager:

```bash
# Install uv
curl -LsSf https://astral.sh/uv/install.sh | sh

# Install FastMVC
uv pip install pyfastmvc
```

### Using pip

```bash
pip install pyfastmvc
```

Verify installation:

```bash
fastmvc --version
# → fastmvc, version 1.2.0
```

---

## ⚡ Quick Start

### 1️⃣ Create a New Project

```bash
fastmvc generate my_api
```

### 2️⃣ Setup & Run

```bash
cd my_api

# Using uv (recommended)
uv sync
uv run fastmvc migrate upgrade
uv run uvicorn app:app --reload

# Or using pip
pip install -r requirements.txt
cp .env.example .env
docker-compose up -d          # Start PostgreSQL + Redis
fastmvc migrate upgrade       # Run migrations
python -m uvicorn app:app --reload
```

### 3️⃣ Done! 🎉

Your API is running at:
- **API:** http://localhost:8000
- **Docs:** http://localhost:8000/docs
- **ReDoc:** http://localhost:8000/redoc

---

## ✨ Features

<table>
<tr>
<td width="50%">

### 🏗️ MVC Architecture
Clean separation with Controllers, Services, and Repositories

```python
# controllers/user/login.py
class UserLoginController(IController):
    async def post(self, request: LoginDTO):
        return await self.service.run(request)
```

</td>
<td width="50%">

### ⚡ CLI Scaffolding
Generate complete CRUD in seconds

```bash
fastmvc add entity Product
# Creates: model, repo, service,
# controller, DTOs, and tests!
```

</td>
</tr>
<tr>
<td width="50%">

### 🔐 Security Built-In
JWT auth, rate limiting, security headers

```python
# Automatic JWT protection
@router.post("/protected")
async def secure_endpoint(
    user: User = Depends(get_current_user)
):
    return {"user": user.email}
```

</td>
<td width="50%">

### 💾 Smart Caching
Redis caching with decorators

```python
@cache.cached(ttl=300, prefix="user")
async def get_user(user_id: int):
    return await db.fetch_user(user_id)
```

</td>
</tr>
<tr>
<td width="50%">

### 🗄️ Easy Migrations
Alembic migrations simplified

```bash
fastmvc migrate generate "add products"
fastmvc migrate upgrade
fastmvc migrate status
```

</td>
<td width="50%">

### 📝 Type Safety
Full Pydantic v2 validation

```python
class UserDTO(BaseRequestDTO):
    email: EmailStr
    password: str = Field(min_length=8)
```

</td>
</tr>
</table>

---

## 🛠️ CLI Commands

### Project Management

```bash
# Create a new project
fastmvc generate my_project

# With all options
fastmvc generate my_project \
    --output-dir ~/projects \
    --git \      # Initialize git repo
    --venv \     # Create virtual environment
    --install    # Install dependencies
```

### Entity Generation

```bash
# Generate complete CRUD for an entity
fastmvc add entity Product
```

This creates:
```
📁 Generated Files:
├── models/product.py           # SQLAlchemy model
├── repositories/product.py     # Data access layer
├── services/product/           # Business logic
│   ├── abstraction.py
│   └── crud.py
├── controllers/product/        # API endpoints
├── dtos/requests/product/      # Request DTOs
│   ├── create.py
│   └── update.py
└── tests/unit/.../test_product.py
```

### Database Migrations

```bash
fastmvc migrate generate "add product table"  # Create migration
fastmvc migrate upgrade                        # Apply migrations
fastmvc migrate downgrade                      # Rollback one step
fastmvc migrate status                         # Show current status
fastmvc migrate history                        # Show all migrations
```

---

## 🏛️ Architecture Overview

FastMVC implements a clean **Model-View-Controller (MVC)** pattern optimized for APIs:

```
┌──────────────────────────────────────────────────────────────────────────────────────────┐
│                                    🌐 CLIENT                                              │
│                          (Browser, Mobile App, API Consumer)                              │
└──────────────────────────────────────────────────────────────────────────────────────────┘
                                           │
                                           │ HTTP/HTTPS
                                           ▼
┌──────────────────────────────────────────────────────────────────────────────────────────┐
│                                 ⚡ FASTAPI APPLICATION                                    │
│  ┌────────────────────────────────────────────────────────────────────────────────────┐  │
│  │                            🛡️ MIDDLEWARE PIPELINE                                   │  │
│  │  ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────────┐ │  │
│  │  │ Request  │→│  Timing  │→│  Rate    │→│   JWT    │→│ Logging  │→│   Security   │ │  │
│  │  │ Context  │ │          │ │  Limit   │ │   Auth   │ │          │ │   Headers    │ │  │
│  │  │ (URN)    │ │ (Perf)   │ │ (Abuse)  │ │ (Token)  │ │ (Audit)  │ │ (CSP/HSTS)   │ │  │
│  │  └──────────┘ └──────────┘ └──────────┘ └──────────┘ └──────────┘ └──────────────┘ │  │
│  └────────────────────────────────────────────────────────────────────────────────────┘  │
│                                           │                                               │
│                                           ▼                                               │
│  ┌────────────────────────────────────────────────────────────────────────────────────┐  │
│  │                              🎮 CONTROLLER LAYER                                    │  │
│  │                                                                                     │  │
│  │   ┌─────────────────┐   ┌─────────────────┐   ┌─────────────────┐                  │  │
│  │   │  UserLogin      │   │  UserRegister   │   │  ProductCRUD    │   ...            │  │
│  │   │  Controller     │   │  Controller     │   │  Controller     │                  │  │
│  │   └────────┬────────┘   └────────┬────────┘   └────────┬────────┘                  │  │
│  │            │ Validate & Route    │                     │                           │  │
│  └────────────┼─────────────────────┼─────────────────────┼───────────────────────────┘  │
│               │                     │                     │                               │
│               ▼                     ▼                     ▼                               │
│  ┌────────────────────────────────────────────────────────────────────────────────────┐  │
│  │                               🔧 SERVICE LAYER                                      │  │
│  │                           (Business Logic & Rules)                                  │  │
│  │                                                                                     │  │
│  │   ┌─────────────────┐   ┌─────────────────┐   ┌─────────────────┐                  │  │
│  │   │  UserLogin      │   │ UserRegistration│   │  ProductCRUD    │   ...            │  │
│  │   │  Service        │   │  Service        │   │  Service        │                  │  │
│  │   └────────┬────────┘   └────────┬────────┘   └────────┬────────┘                  │  │
│  │            │ Business Logic      │                     │                           │  │
│  └────────────┼─────────────────────┼─────────────────────┼───────────────────────────┘  │
│               │                     │                     │                               │
│               ▼                     ▼                     ▼                               │
│  ┌────────────────────────────────────────────────────────────────────────────────────┐  │
│  │                              🗄️ REPOSITORY LAYER                                    │  │
│  │                            (Data Access Abstraction)                                │  │
│  │                                                                                     │  │
│  │   ┌─────────────────┐   ┌─────────────────────────────────────────────────────┐    │  │
│  │   │ UserRepository  │   │              IRepository (Base)                     │    │  │
│  │   │                 │   │  • retrieve_record_by_filter(filters, operators)    │    │  │
│  │   │ • find_by_email │   │  • create_record(data)                              │    │  │
│  │   │ • check_login   │   │  • update_record_by_filter(filters, data)           │    │  │
│  │   │ • update_status │   │  • delete_record_by_filter(filters)                 │    │  │
│  │   └────────┬────────┘   │  • count_by_filter(filters)                         │    │  │
│  │            │            │  • exists_by_filter(filters)                        │    │  │
│  │            │            └─────────────────────────────────────────────────────┘    │  │
│  └────────────┼───────────────────────────────────────────────────────────────────────┘  │
│               │                                                                           │
└───────────────┼───────────────────────────────────────────────────────────────────────────┘
                │
                ▼
┌──────────────────────────────────────────────────────────────────────────────────────────┐
│                                  💾 DATA LAYER                                            │
│                                                                                           │
│   ┌─────────────────────────────────┐       ┌─────────────────────────────────┐          │
│   │        🐘 PostgreSQL            │       │          🔴 Redis               │          │
│   │                                 │       │                                 │          │
│   │  ┌───────────┐ ┌───────────┐   │       │  ┌───────────┐ ┌───────────┐   │          │
│   │  │   users   │ │ products  │   │       │  │  Session  │ │   Cache   │   │          │
│   │  │   table   │ │   table   │   │       │  │   Store   │ │   Store   │   │          │
│   │  └───────────┘ └───────────┘   │       │  └───────────┘ └───────────┘   │          │
│   │                                 │       │                                 │          │
│   │  SQLAlchemy ORM + Alembic      │       │  Rate Limit + Caching          │          │
│   └─────────────────────────────────┘       └─────────────────────────────────┘          │
│                                                                                           │
└──────────────────────────────────────────────────────────────────────────────────────────┘
```

---

## 🔄 Request Lifecycle

### Complete Request → Response Flow

```
 ┌─────────────────────────────────────────────────────────────────────────────────────┐
 │                              📥 INCOMING REQUEST                                     │
 │                         POST /user/login { email, password }                         │
 └─────────────────────────────────────────────────────────────────────────────────────┘
                                          │
      ┌───────────────────────────────────┼───────────────────────────────────┐
      │                                   ▼                                   │
      │  ╔═══════════════════════════════════════════════════════════════╗   │
      │  ║              1️⃣  REQUEST CONTEXT MIDDLEWARE                    ║   │
      │  ╠═══════════════════════════════════════════════════════════════╣   │
      │  ║  • Generate unique URN: 01ARZ3NDEKTSV4RRFFQ69G5FAV            ║   │
      │  ║  • Record start timestamp                                      ║   │
      │  ║  • Attach to request.state for tracing                        ║   │
      │  ╚═══════════════════════════════════════════════════════════════╝   │
      │                                   │                                   │
      │                                   ▼                                   │
      │  ╔═══════════════════════════════════════════════════════════════╗   │
      │  ║              2️⃣  TIMING MIDDLEWARE                             ║   │
      │  ╠═══════════════════════════════════════════════════════════════╣   │
      │  ║  • Start performance timer                                     ║   │
      │  ║  • Will add X-Process-Time header on response                 ║   │
      │  ╚═══════════════════════════════════════════════════════════════╝   │
      │                                   │                                   │
      │                                   ▼                                   │
      │  ╔═══════════════════════════════════════════════════════════════╗   │
      │  ║              3️⃣  RATE LIMIT MIDDLEWARE                         ║   │
      │  ╠═══════════════════════════════════════════════════════════════╣   │
      │  ║  • Check sliding window: 60 req/min, 1000 req/hour            ║   │
      │  ║  • If exceeded → 429 Too Many Requests                        ║   │
      │  ║  • Add headers: X-RateLimit-Remaining, Retry-After            ║   │
      │  ╚═══════════════════════════════════════════════════════════════╝   │
      │                                   │                                   │
      │                                   ▼                                   │
      │  ╔═══════════════════════════════════════════════════════════════╗   │
      │  ║              4️⃣  AUTHENTICATION MIDDLEWARE                     ║   │
      │  ╠═══════════════════════════════════════════════════════════════╣   │
      │  ║  • Check if route is protected                                 ║   │
      │  ║  • /user/login is UNPROTECTED → skip JWT validation           ║   │
      │  ║  • Protected routes: validate JWT, check user session         ║   │
      │  ╚═══════════════════════════════════════════════════════════════╝   │
      │                                   │                                   │
  M   │                                   ▼                                   │
  I   │  ╔═══════════════════════════════════════════════════════════════╗   │
  D   │  ║              5️⃣  LOGGING MIDDLEWARE                            ║   │
  D   │  ╠═══════════════════════════════════════════════════════════════╣   │
  L   │  ║  • Log: method, path, client IP, user agent                   ║   │
  E   │  ║  • Structured JSON logging for observability                  ║   │
  W   │  ╚═══════════════════════════════════════════════════════════════╝   │
  A   │                                   │                                   │
  R   │                                   ▼                                   │
  E   │  ╔═══════════════════════════════════════════════════════════════╗   │
      │  ║              6️⃣  CORS & SECURITY HEADERS                       ║   │
      │  ╠═══════════════════════════════════════════════════════════════╣   │
      │  ║  • CORS: Allow-Origin, Allow-Methods, Allow-Headers           ║   │
      │  ║  • HSTS: Strict-Transport-Security                            ║   │
      │  ║  • CSP: Content-Security-Policy                               ║   │
      │  ║  • X-Frame-Options, X-Content-Type-Options                    ║   │
      │  ╚═══════════════════════════════════════════════════════════════╝   │
      │                                   │                                   │
      └───────────────────────────────────┼───────────────────────────────────┘
                                          │
                                          ▼
 ┌─────────────────────────────────────────────────────────────────────────────────────┐
 │                           🎮 CONTROLLER: UserLoginController                         │
 ├─────────────────────────────────────────────────────────────────────────────────────┤
 │                                                                                      │
 │   async def post(self, request: UserLoginRequestDTO):                               │
 │       ┌──────────────────────────────────────────────────────────────────┐          │
 │       │  1. Validate request payload (Pydantic v2)                       │          │
 │       │     • email: EmailStr ✓                                          │          │
 │       │     • password: str (min 8 chars) ✓                              │          │
 │       ├──────────────────────────────────────────────────────────────────┤          │
 │       │  2. Call service layer                                           │          │
 │       │     result = await self.login_service.run(request)               │          │
 │       ├──────────────────────────────────────────────────────────────────┤          │
 │       │  3. Format response                                              │          │
 │       │     return BaseResponseDTO(status="SUCCESS", data=result)        │          │
 │       └──────────────────────────────────────────────────────────────────┘          │
 │                                                                                      │
 └─────────────────────────────────────────────────────────────────────────────────────┘
                                          │
                                          ▼
 ┌─────────────────────────────────────────────────────────────────────────────────────┐
 │                           🔧 SERVICE: UserLoginService                               │
 ├─────────────────────────────────────────────────────────────────────────────────────┤
 │                                                                                      │
 │   async def run(self, request_dto: UserLoginRequestDTO):                            │
 │       ┌──────────────────────────────────────────────────────────────────┐          │
 │       │  1. Find user by email                                           │          │
 │       │     user = self.user_repo.retrieve_record_by_filter(             │          │
 │       │         {"email": request_dto.email}                             │          │
 │       │     )                                                            │          │
 │       │     if not user: raise NotFoundError("User not found")           │          │
 │       ├──────────────────────────────────────────────────────────────────┤          │
 │       │  2. Verify password                                              │          │
 │       │     if not bcrypt.verify(request_dto.password, user.password):   │          │
 │       │         raise AuthenticationError("Invalid credentials")         │          │
 │       ├──────────────────────────────────────────────────────────────────┤          │
 │       │  3. Generate JWT token                                           │          │
 │       │     token = self.jwt_utility.create_token(                       │          │
 │       │         user_id=user.id, user_urn=user.urn                       │          │
 │       │     )                                                            │          │
 │       ├──────────────────────────────────────────────────────────────────┤          │
 │       │  4. Update login status                                          │          │
 │       │     self.user_repo.update_record_by_filter(                      │          │
 │       │         {"id": user.id}, {"is_logged_in": True}                  │          │
 │       │     )                                                            │          │
 │       ├──────────────────────────────────────────────────────────────────┤          │
 │       │  5. Return result                                                │          │
 │       │     return {"user": user.to_dict(), "token": token}              │          │
 │       └──────────────────────────────────────────────────────────────────┘          │
 │                                                                                      │
 └─────────────────────────────────────────────────────────────────────────────────────┘
                                          │
                                          ▼
 ┌─────────────────────────────────────────────────────────────────────────────────────┐
 │                          🗄️ REPOSITORY: UserRepository                               │
 ├─────────────────────────────────────────────────────────────────────────────────────┤
 │                                                                                      │
 │   def retrieve_record_by_filter(self, filters, use_or=False):                       │
 │       ┌──────────────────────────────────────────────────────────────────┐          │
 │       │  1. Build SQLAlchemy query                                       │          │
 │       │     query = self.session.query(User)                             │          │
 │       │                                                                  │          │
 │       │  2. Apply filters with operators                                 │          │
 │       │     Supports: EQ, NE, GT, LT, GTE, LTE, LIKE, IN, IS_NULL       │          │
 │       │     query = query.filter(User.email == "user@example.com")       │          │
 │       │                                                                  │          │
 │       │  3. Execute and return                                           │          │
 │       │     return query.first()                                         │          │
 │       └──────────────────────────────────────────────────────────────────┘          │
 │                                          │                                          │
 │                                          ▼                                          │
 │                              ┌───────────────────────┐                              │
 │                              │   🐘 PostgreSQL       │                              │
 │                              │   SELECT * FROM users │                              │
 │                              │   WHERE email = ?     │                              │
 │                              └───────────────────────┘                              │
 │                                                                                      │
 └─────────────────────────────────────────────────────────────────────────────────────┘
                                          │
                                          ▼
 ┌─────────────────────────────────────────────────────────────────────────────────────┐
 │                              📤 OUTGOING RESPONSE                                    │
 ├─────────────────────────────────────────────────────────────────────────────────────┤
 │                                                                                      │
 │   HTTP/1.1 200 OK                                                                   │
 │   Content-Type: application/json                                                    │
 │   X-Request-ID: 01ARZ3NDEKTSV4RRFFQ69G5FAV                                         │
 │   X-Process-Time: 0.045s                                                            │
 │   X-RateLimit-Remaining: 59                                                         │
 │   Strict-Transport-Security: max-age=31536000; includeSubDomains                   │
 │   Content-Security-Policy: default-src 'self'                                       │
 │                                                                                      │
 │   {                                                                                  │
 │       "transactionUrn": "01ARZ3NDEKTSV4RRFFQ69G5FAV",                              │
 │       "status": "SUCCESS",                                                          │
 │       "responseMessage": "User logged in successfully",                             │
 │       "responseKey": "success_user_login",                                          │
 │       "data": {                                                                      │
 │           "user": { "id": 1, "email": "user@example.com" },                         │
 │           "token": "eyJhbGciOiJIUzI1NiIs..."                                        │
 │       }                                                                              │
 │   }                                                                                  │
 │                                                                                      │
 └─────────────────────────────────────────────────────────────────────────────────────┘
```

---

## 🔀 MVC Layer Interactions

```
┌─────────────────────────────────────────────────────────────────────────────────────┐
│                                                                                      │
│    📨 DTOs                    🎮 CONTROLLER                   🔧 SERVICE            │
│   (Validation)               (HTTP Handler)               (Business Logic)          │
│                                                                                      │
│  ┌─────────────┐            ┌─────────────┐              ┌─────────────┐            │
│  │ RequestDTO  │───────────▶│  validate   │─────────────▶│    run()    │            │
│  │             │  Pydantic  │  & parse    │   Inject     │             │            │
│  │ • email     │  validates │             │   service    │ • business  │            │
│  │ • password  │            │  post()     │              │   rules     │            │
│  │ • metadata  │            │  get()      │