SimpleFastCrud is a tool that simplifies the creation of dynamic CRUD endpoints in FastAPI using SQLAlchemy and Pydantic.
Project description
SimpleFastCrud
SimpleFastCrud is a powerful tool that automatically generates complete CRUD (Create, Read, Update, Delete) endpoints for your FastAPI applications using SQLAlchemy models. It eliminates boilerplate code and provides advanced features like filtering, pagination, multi-tenancy support, and relationship handling.
Features
- 🚀 Automatic CRUD endpoint generation
- 🔍 Advanced filtering and search capabilities
- 📄 Built-in pagination support
- 🏢 Multi-tenant application support
- 🔐 Authentication and authorization integration
- 🔗 Automatic relationship handling with eager loading
- 📊 Customizable query parameters
Installation
pip install SimpleFastCrud
Requirements
- FastAPI
- SQLAlchemy
- Pydantic
Quick Start
Basic Setup
from fastapi import APIRouter, FastAPI
from sqlalchemy import Column, Integer, String, create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
from SimpleFastCrud import SimpleFastCrud
# Database setup
Base = declarative_base()
engine = create_engine('sqlite:///./test.db')
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
# Database dependency
def get_db():
db = SessionLocal()
try:
yield db
finally:
db.close()
# FastAPI setup
app = FastAPI()
api_router = APIRouter()
# Define your model
class User(Base):
__tablename__ = 'users'
id = Column(Integer, primary_key=True, index=True)
name = Column(String, index=True)
email = Column(String, unique=True, index=True)
# Create tables
Base.metadata.create_all(bind=engine)
# Initialize SimpleFastCrud and add your model
crud = SimpleFastCrud(api_router=api_router, get_db=get_db)
crud.add(model=User)
# Include the router in your app
app.include_router(api_router)
This simple setup automatically generates the following endpoints:
GET /users- List all users (with pagination and filtering)GET /users/{id}- Get a specific user by IDPOST /users- Create a new userPUT /users/{id}- Update an existing userDELETE /users/{id}- Delete a user
Advanced Features
1. Authentication and Authorization (auth_dep)
Add authentication to your CRUD endpoints by providing an authentication dependency. This ensures that only authenticated users can access the endpoints.
from fastapi import Depends, HTTPException
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
security = HTTPBearer()
def get_current_user(credentials: HTTPAuthorizationCredentials = Depends(security)):
"""
Verify the authentication token and return user information.
This is a simplified example - implement your own authentication logic.
"""
token = credentials.credentials
# Verify token here (JWT, OAuth, etc.)
# For this example, we'll return a mock user
if token != "valid_token":
raise HTTPException(status_code=401, detail="Invalid authentication")
return {
"user_id": 1,
"username": "john_doe",
"email": "john@example.com"
}
# Add CRUD with authentication
crud.add(
model=User,
auth_dep=Depends(get_current_user)
)
What it does:
- All endpoints will require authentication
- The authenticated user information will be available in the endpoint handlers
- Unauthenticated requests will receive a 401 error
2. Multi-Tenant Filtering (filter_param)
Perfect for SaaS applications where data must be isolated per tenant/organization. This parameter automatically filters all queries based on a field in your model.
from sqlalchemy import Column, Integer, String, ForeignKey
class Product(Base):
__tablename__ = 'products'
id = Column(Integer, primary_key=True)
name = Column(String)
price = Column(Integer)
tenant_id = Column(Integer, index=True) # The tenant identifier
def get_current_user(credentials: HTTPAuthorizationCredentials = Depends(security)):
# Your authentication logic
return {
"user_id": 1,
"tenant_id": 42, # The tenant this user belongs to
"username": "john_doe"
}
# Add CRUD with multi-tenant support
crud.add(
model=Product,
auth_dep=Depends(get_current_user),
filter_param='tenant_id' # Automatically filter by tenant_id
)
What it does:
- GET requests: Only returns records where
tenant_idmatches the authenticated user'stenant_id - POST requests: Automatically sets the
tenant_idfrom the authenticated user - PUT/DELETE requests: Only allows modifications to records belonging to the user's tenant
- Prevents data leakage between tenants
Example:
- User from Tenant A cannot see, modify, or delete products from Tenant B
- When creating a product, the
tenant_idis automatically set from the user's token
3. Search Functionality (filter_query_search)
Enable text search on a specific field. Users can search using the search query parameter.
class Article(Base):
__tablename__ = 'articles'
id = Column(Integer, primary_key=True)
title = Column(String)
content = Column(String)
author = Column(String)
crud.add(
model=Article,
filter_query_search='title' # Enable search on the title field
)
How to use:
# Search for articles with "Python" in the title
GET /articles?search=Python
# The search is case-insensitive and uses LIKE %search%
What it does:
- Adds a
searchquery parameter to the GET endpoint - Performs case-insensitive partial matching (ILIKE)
- Example:
search=pythonwill find "Introduction to Python", "Python Basics", etc.
4. Advanced Filtering (filter_fields)
Create custom filters with different comparison operators. Users can filter results using query parameters.
from sqlalchemy import Column, Integer, String, Date, Float
class Order(Base):
__tablename__ = 'orders'
id = Column(Integer, primary_key=True)
customer_name = Column(String)
total = Column(Float)
status = Column(String)
created_at = Column(Date)
crud.add(
model=Order,
filter_fields={
'status': {
'comparison': 'eq', # Equal comparison
'type': 'string',
'description': 'Filter by order status (pending, completed, cancelled)'
},
'total': {
'comparison': 'ge', # Greater than or equal
'type': 'number',
'description': 'Minimum order total'
},
'created_at': {
'comparison': 'between', # Date range
'type': 'date',
'description': 'Filter by creation date range'
}
}
)
Available comparison operators:
eq: Equal to (=)ne: Not equal to (!=)lt: Less than (<)le: Less than or equal (<=)gt: Greater than (>)ge: Greater than or equal (>=)between: Range query (for dates and numbers)
How to use:
# Filter by status
GET /orders?status=completed
# Filter by minimum total
GET /orders?total=100
# Filter by date range (format: DD-MM-YYYY)
GET /orders?created_at_from=01-01-2024&created_at_to=31-12-2024
# Combine multiple filters
GET /orders?status=completed&total=100&created_at_from=01-01-2024
5. Pagination
Enable pagination to handle large datasets efficiently.
crud.add(
model=User,
pagination=True, # Enable pagination
steps=25 # Items per page (default: 10)
)
How to use:
# Get first page (25 items)
GET /users?page=1&per_page=25
# Get second page
GET /users?page=2&per_page=25
# Disable pagination for a specific request
GET /users?pagination=false
Response format with pagination:
{
"data": [...],
"message": "",
"metadata": {
"page": 1,
"per_page": 25,
"total": 150,
"total_pages": 6
}
}
Response format without pagination:
{
"data": [...],
"message": "",
"metadata": {
"total": 150
}
}
6. Relationship Handling
By default, SimpleFastCrud automatically loads related models using eager loading (joinedload).
from sqlalchemy.orm import relationship
class Author(Base):
__tablename__ = 'authors'
id = Column(Integer, primary_key=True)
name = Column(String)
books = relationship("Book", back_populates="author")
class Book(Base):
__tablename__ = 'books'
id = Column(Integer, primary_key=True)
title = Column(String)
author_id = Column(Integer, ForeignKey('authors.id'))
author = relationship("Author", back_populates="books")
# With relationships (default)
crud.add(
model=Author,
relationship=True # Includes related books in the response
)
# Without relationships (faster for simple queries)
crud.add(
model=Book,
relationship=False # Only returns book data, no author details
)
What it does:
relationship=True: Uses SQLAlchemy'sjoinedloadto fetch related data in a single queryrelationship=False: Only fetches the main model data (better performance if you don't need related data)
7. Custom Pydantic Schemas
Override the auto-generated schemas with your own Pydantic models.
from pydantic import BaseModel, EmailStr, validator
class UserCreate(BaseModel):
name: str
email: EmailStr
@validator('name')
def name_must_not_be_empty(cls, v):
if not v.strip():
raise ValueError('Name cannot be empty')
return v
crud.add(
model=User,
schema=UserCreate # Use custom schema for input and output
)
Complete Example: E-commerce API
Here's a comprehensive example combining multiple features:
from fastapi import APIRouter, Depends, FastAPI, HTTPException
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
from sqlalchemy import Column, Integer, String, Float, Date, ForeignKey, create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker, relationship
from SimpleFastCrud import SimpleFastCrud
# Database setup
Base = declarative_base()
engine = create_engine('sqlite:///./ecommerce.db')
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
def get_db():
db = SessionLocal()
try:
yield db
finally:
db.close()
# Authentication
security = HTTPBearer()
def get_current_user(credentials: HTTPAuthorizationCredentials = Depends(security)):
# Implement your JWT/OAuth validation here
return {
"user_id": 1,
"tenant_id": 42, # For multi-tenancy
"username": "shop_owner"
}
# Models
class Product(Base):
__tablename__ = 'products'
id = Column(Integer, primary_key=True)
name = Column(String, index=True)
description = Column(String)
price = Column(Float)
stock = Column(Integer)
category = Column(String)
tenant_id = Column(Integer, index=True) # Multi-tenant field
orders = relationship("Order", back_populates="product")
class Order(Base):
__tablename__ = 'orders'
id = Column(Integer, primary_key=True)
customer_name = Column(String)
product_id = Column(Integer, ForeignKey('products.id'))
quantity = Column(Integer)
total = Column(Float)
status = Column(String)
created_at = Column(Date)
tenant_id = Column(Integer, index=True)
product = relationship("Product", back_populates="orders")
# Create tables
Base.metadata.create_all(bind=engine)
# FastAPI setup
app = FastAPI(title="E-commerce API")
api_router = APIRouter()
# Initialize CRUD
crud = SimpleFastCrud(api_router=api_router, get_db=get_db)
# Products CRUD with full features
crud.add(
model=Product,
auth_dep=Depends(get_current_user), # Require authentication
filter_param='tenant_id', # Multi-tenant filtering
filter_query_search='name', # Search by product name
filter_fields={ # Advanced filtering
'category': {
'comparison': 'eq',
'type': 'string',
'description': 'Filter by category'
},
'price': {
'comparison': 'le',
'type': 'number',
'description': 'Maximum price'
}
},
pagination=True, # Enable pagination
steps=20, # 20 items per page
relationship=True # Include related orders
)
# Orders CRUD
crud.add(
model=Order,
auth_dep=Depends(get_current_user),
filter_param='tenant_id',
filter_query_search='customer_name',
filter_fields={
'status': {
'comparison': 'eq',
'type': 'string',
'description': 'Filter by order status'
},
'created_at': {
'comparison': 'between',
'type': 'date',
'description': 'Filter by date range'
},
'total': {
'comparison': 'ge',
'type': 'number',
'description': 'Minimum order total'
}
},
pagination=True,
steps=50
)
app.include_router(api_router)
if __name__ == '__main__':
import uvicorn
uvicorn.run(app, host='0.0.0.0', port=8000)
Example API calls:
# Get products with search and filters
GET /products?search=laptop&category=electronics&price=1000&page=1
# Get orders by date range and status
GET /orders?status=completed&created_at_from=01-01-2024&created_at_to=31-12-2024&page=1
# Create a new product (automatically sets tenant_id from authenticated user)
POST /products
{
"name": "Gaming Laptop",
"description": "High-performance laptop",
"price": 1299.99,
"stock": 10,
"category": "electronics"
}
# Update a product (only if it belongs to your tenant)
PUT /products/1
{
"price": 1199.99,
"stock": 8
}
API Response Format
All endpoints return a consistent response format:
{
"data": {}, // or [] for lists
"message": "", // Success or error message
"metadata": {} // Pagination info, counts, etc.
}
Parameters Reference
add() Method Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
model |
SQLAlchemy Model | Required | The SQLAlchemy model class |
auth_dep |
Depends | None | Authentication dependency for securing endpoints |
filter_param |
str | None | Field name for multi-tenant filtering (e.g., 'tenant_id') |
schema |
Pydantic Model | None | Custom Pydantic schema (auto-generated if not provided) |
dependencies |
List[Depends] | [] | Additional FastAPI dependencies |
relationship |
bool | True | Enable/disable eager loading of relationships |
pagination |
bool | False | Enable pagination for GET all endpoint |
steps |
int | 10 | Default number of items per page |
filter_query_search |
str | None | Field name for text search functionality |
filter_fields |
dict | None | Advanced filtering configuration |
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
License
This project is licensed under the MIT License.
Support
If you encounter any issues or have questions, please open an issue on the GitHub repository.
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
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file simplefastcrud-0.1.5.tar.gz.
File metadata
- Download URL: simplefastcrud-0.1.5.tar.gz
- Upload date:
- Size: 17.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
fda486d6e8b7adf35fa04ab83f3667ee6c9b28730e1adca29fc9a37afd7cc8e5
|
|
| MD5 |
11a6972aaeb751267314dfcd29ec4c91
|
|
| BLAKE2b-256 |
c8542b34e191910535e9c56a5873d059c9c8d6c6802248ab5b098fc77d8a6615
|
File details
Details for the file simplefastcrud-0.1.5-py3-none-any.whl.
File metadata
- Download URL: simplefastcrud-0.1.5-py3-none-any.whl
- Upload date:
- Size: 11.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6ffbfbdab18afea809ba80bf58b25e26561ee7b18072edcd6e8823739919d2ee
|
|
| MD5 |
9870b58d48ef64fe0af36b7fd38e9328
|
|
| BLAKE2b-256 |
7d8d90d2e9c46a6d483071f509effdfa405c942d2050ac7dfeb8e4998a5c96b6
|