Skip to main content

Utilities and base classes for FastAPI async projects (Beanie or SQLAlchemy)

Project description

🚀 FastAPI BaseKit

FastAPI Python SQLAlchemy MongoDB License

Toolkit base para desarrollo rápido de APIs REST con FastAPI

DocumentaciónEjemplosChangelogContribuir


✨ Características

  • 🎯 CRUD Automático: Controllers base con operaciones CRUD listas para usar
  • 🔍 Búsqueda Inteligente: Búsqueda multi-campo con filtros dinámicos
  • 📊 Paginación Avanzada: Paginación automática con has_next, has_prev
  • 🔗 Relaciones Optimizadas: Joins dinámicos para evitar queries N+1
  • 🎨 Type-Safe: Type hints completos para mejor DX
  • 🧪 Testeable: Diseño que facilita testing
  • 🗃️ Multi-DB: Soporte para SQLAlchemy (Postgres, MySQL, etc.) y MongoDB
  • 🔒 Permisos: Sistema de permisos basado en clases
  • Performance: Queries optimizados y lazy loading
  • 📝 Validación: Validación automática con Pydantic

📦 Instalación

# Instalación básica
pip install fastapi-basekit

# Con soporte SQLAlchemy
pip install fastapi-basekit[sqlalchemy]

# Con soporte MongoDB
pip install fastapi-basekit[mongodb]

# Con todo
pip install fastapi-basekit[all]

🚀 Inicio Rápido

1. Modelo (SQLAlchemy)

# models/user.py
from sqlalchemy import Column, String, Boolean
from database import Base

class User(Base):
    __tablename__ = "users"

    id = Column(String, primary_key=True)
    name = Column(String, nullable=False)
    email = Column(String, unique=True, nullable=False)
    is_active = Column(Boolean, default=True)

2. Schema (Pydantic)

# schemas/user.py
from pydantic import BaseModel, EmailStr

class UserSchema(BaseModel):
    id: str
    name: str
    email: EmailStr
    is_active: bool

class UserCreateSchema(BaseModel):
    name: str
    email: EmailStr

3. Repository

# repositories/user.py
from fastapi_basekit.aio.sqlalchemy.repository.base import BaseRepository
from models.user import User

class UserRepository(BaseRepository):
    model = User

4. Service

# services/user.py
from fastapi_basekit.aio.sqlalchemy.service.base import BaseService

class UserService(BaseService):
    search_fields = ["name", "email"]
    duplicate_check_fields = ["email"]

5. Controller

# controllers/user.py
from fastapi import APIRouter, Query, Depends
from fastapi_basekit.aio.sqlalchemy.controller.base import SQLAlchemyBaseController
from schemas.user import UserSchema, UserCreateSchema
from services.user import UserService

router = APIRouter(prefix="/users", tags=["users"])

@router.get("/")
class ListUsers(SQLAlchemyBaseController):
    schema_class = UserSchema
    service = Depends(UserService)

    async def __call__(
        self,
        page: int = Query(1, ge=1),
        count: int = Query(10, ge=1, le=100),
        search: str = Query(None),
        is_active: bool = Query(None),
        order_by: str = Query(None),
    ):
        return await self.list()

@router.get("/{id}")
class GetUser(SQLAlchemyBaseController):
    schema_class = UserSchema
    service = Depends(UserService)

    async def __call__(self, id: str):
        return await self.retrieve(id)

@router.post("/")
class CreateUser(SQLAlchemyBaseController):
    schema_class = UserSchema
    service = Depends(UserService)

    async def __call__(self, data: UserCreateSchema):
        return await self.create(data)

6. ¡Listo! 🎉

Ya tienes un CRUD completo con:

✅ Paginación automática
✅ Búsqueda por nombre o email
✅ Filtrado por is_active
✅ Ordenamiento configurable
✅ Validación de duplicados
✅ Type hints completos


📚 Ejemplos de Uso

Listar con Filtros y Paginación

# Página 1, 10 items
GET /users?page=1&count=10

# Buscar usuarios
GET /users?search=john

# Filtrar activos
GET /users?is_active=true

# Ordenar por nombre
GET /users?order_by=name&order_direction=asc

# Combinar filtros
GET /users?search=john&is_active=true&order_by=created_at&order_direction=desc

Respuesta:

{
  "data": [
    {
      "id": "123",
      "name": "John Doe",
      "email": "john@example.com",
      "is_active": true
    }
  ],
  "pagination": {
    "page": 1,
    "count": 10,
    "total": 100,
    "pages": 10,
    "total_pages": 10,
    "has_next": true,
    "has_prev": false,
    "next_page": 2,
    "prev_page": null
  },
  "message": "Operación exitosa",
  "status": "success"
}

Obtener un Usuario

GET /users/123

Respuesta:

{
  "data": {
    "id": "123",
    "name": "John Doe",
    "email": "john@example.com",
    "is_active": true
  },
  "message": "Operación exitosa",
  "status": "success"
}

Crear Usuario

POST /users
Content-Type: application/json

{
  "name": "Jane Doe",
  "email": "jane@example.com"
}

Respuesta:

{
  "data": {
    "id": "124",
    "name": "Jane Doe",
    "email": "jane@example.com",
    "is_active": true
  },
  "message": "Creado exitosamente",
  "status": "success"
}

🎯 Características Avanzadas

Relaciones y Joins

# models/user.py
class User(Base):
    __tablename__ = "users"

    id = Column(String, primary_key=True)
    name = Column(String)
    role_id = Column(String, ForeignKey("roles.id"))

    # Relación
    role = relationship("Role", back_populates="users")

# controller
@router.get("/")
async def list_users(
    self,
    include_role: bool = Query(False),
):
    # Si include_role=true, carga la relación automáticamente
    if include_role:
        self.service.kwargs_query = {"joins": ["role"]}
    return await self.list()

Permisos Personalizados

from fastapi_basekit.aio.permissions.base import BasePermission

class IsAdmin(BasePermission):
    message_exception = "Solo administradores pueden acceder"

    async def has_permission(self, request: Request) -> bool:
        user = request.state.user
        return user.is_admin

class UserController(SQLAlchemyBaseController):
    schema_class = UserSchema
    service = Depends(UserService)

    def check_permissions(self) -> List[Type[BasePermission]]:
        if self.action in ["create", "delete"]:
            return [IsAdmin]
        return []

Soft Deletes

# models/user.py
class User(Base):
    __tablename__ = "users"

    id = Column(String, primary_key=True)
    name = Column(String)
    deleted_at = Column(DateTime, nullable=True)

# repository
class UserRepository(BaseRepository):
    model = User
    enable_soft_delete = True

# controller
@router.delete("/{id}")
async def delete_user(self, id: str, hard: bool = False):
    """
    Soft delete por defecto.
    hard=true para eliminación física.
    """
    await self.service.delete(id, hard_delete=hard)
    return self.format_response(None, message="Usuario eliminado")

Validación de Duplicados

class UserService(BaseService):
    # Validar que email sea único antes de crear
    duplicate_check_fields = ["email"]

# Intento de crear usuario con email duplicado
# → DatabaseIntegrityException: "Registro ya existe"

🔧 Configuración

# main.py
from fastapi import FastAPI
from fastapi_basekit import configure

# Configurar el toolkit globalmente
configure(
    default_page_size=25,
    max_page_size=200,
    log_level="INFO",
    strict_filter_validation=True,
)

app = FastAPI(title="Mi API")

Variables de entorno:

# .env
FASTAPI_BASEKIT_DEFAULT_PAGE_SIZE=25
FASTAPI_BASEKIT_MAX_PAGE_SIZE=200
FASTAPI_BASEKIT_LOG_LEVEL=DEBUG
FASTAPI_BASEKIT_STRICT_FILTER_VALIDATION=true

📊 Arquitectura

┌─────────────┐
│   Client    │
└─────┬───────┘
      │ HTTP Request
      ▼
┌─────────────────┐
│   Controller    │  ← Validación, permisos, formato de respuesta
└────────┬────────┘
         │
         ▼
┌─────────────────┐
│    Service      │  ← Lógica de negocio, validaciones
└────────┬────────┘
         │
         ▼
┌─────────────────┐
│   Repository    │  ← Acceso a datos, queries
└────────┬────────┘
         │
         ▼
┌─────────────────┐
│    Database     │
└─────────────────┘

🧪 Testing

# tests/test_user_controller.py
import pytest
from fastapi.testclient import TestClient

def test_list_users(client: TestClient):
    response = client.get("/users?page=1&count=10")
    assert response.status_code == 200
    data = response.json()
    assert "data" in data
    assert "pagination" in data
    assert data["pagination"]["page"] == 1

def test_create_user(client: TestClient):
    user_data = {
        "name": "Test User",
        "email": "test@example.com"
    }
    response = client.post("/users", json=user_data)
    assert response.status_code == 200
    data = response.json()
    assert data["data"]["name"] == "Test User"
    assert data["message"] == "Creado exitosamente"

🤝 Contribuir

¡Las contribuciones son bienvenidas! Por favor lee CONTRIBUTING.md para detalles.

Desarrollo Local

# Clonar
git clone https://github.com/mundobien2025/fastapi-basekit.git
cd fastapi-basekit

# Instalar con Poetry
poetry install

# Activar entorno virtual
poetry shell

# Ejecutar tests
pytest

# Linting
black fastapi_basekit
flake8 fastapi_basekit
mypy fastapi_basekit

📄 Licencia

Este proyecto está licenciado bajo la licencia MIT - ver LICENSE para detalles.


🙏 Agradecimientos

  • FastAPI - El framework web moderno y rápido
  • SQLAlchemy - El ORM SQL para Python
  • Pydantic - Validación de datos usando Python type hints

📞 Soporte


Hecho con ❤️ para la comunidad FastAPI

⭐ Si te gusta este proyecto, dale una estrella en GitHub

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

fastapi_basekit-0.1.15.tar.gz (23.0 kB view details)

Uploaded Source

Built Distribution

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

fastapi_basekit-0.1.15-py3-none-any.whl (23.9 kB view details)

Uploaded Python 3

File details

Details for the file fastapi_basekit-0.1.15.tar.gz.

File metadata

  • Download URL: fastapi_basekit-0.1.15.tar.gz
  • Upload date:
  • Size: 23.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/5.1.1 CPython/3.12.7

File hashes

Hashes for fastapi_basekit-0.1.15.tar.gz
Algorithm Hash digest
SHA256 4a58d78420b2d926b90839c353767a66ad3dbb20dffb25db0deec9e4139600df
MD5 738ca2869efe72976c2e0e1c5b189a43
BLAKE2b-256 b83d866ce1e354f204ff13b2662222e31d6a87ddc43802a58a0ac771b87cee3b

See more details on using hashes here.

Provenance

The following attestation bundles were made for fastapi_basekit-0.1.15.tar.gz:

Publisher: publish.yml on mundobien2025/fastapi-basekit

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file fastapi_basekit-0.1.15-py3-none-any.whl.

File metadata

File hashes

Hashes for fastapi_basekit-0.1.15-py3-none-any.whl
Algorithm Hash digest
SHA256 69ae2bc3578071a112d2193a748594e2600e326c4080b1b26a89f039b8d278d1
MD5 806c4e78e80024eaa87ae7dcfc9d6c28
BLAKE2b-256 db9bf36546aa920ca857735914362b2f35fbcd34a035d21a1085873d69b5e38f

See more details on using hashes here.

Provenance

The following attestation bundles were made for fastapi_basekit-0.1.15-py3-none-any.whl:

Publisher: publish.yml on mundobien2025/fastapi-basekit

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

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