Petrify = make solid/unchanging (REST API standards)
Auto-generate full-featured CRUD APIs from Pydantic models for FastAPI. Pass in a model, get REST and GraphQL routes with advanced querying, associations, RBAC, and AI agent integration.
- Automatic REST API - Full CRUD endpoints from Pydantic models
- Automatic GraphQL API - Queries and mutations with schema generation
- Advanced Query Parsing - Filtering, field selection, aggregation, ordering, grouping
- Multi-Model Associations - One-to-many, many-to-one, one-to-one, many-to-many (opt-in)
- Role-Based Access Control - Operation and resource-level permissions (opt-in)
- MCP Mode - AI agent integration endpoints for automated discovery (opt-in)
- Security First - SQL injection prevention, input validation, sanitization
- CRUD operations (Create, Read, Update, Delete)
- Pagination (offset-based)
- Advanced filtering:
?status=active,pending&role=admin - Field selection:
?select=id,name,email - Aggregations:
?select=count(id) as total&groupBy=category - Ordering:
?orderBy=name:asc,created_at:desc - Include associations:
?include=posts,profile
- Full CRUD with queries and mutations
- Relay-style cursor pagination with connections
- Advanced WHERE filtering with operators (eq, in, lt, gte, etc.)
- Logical operators (AND, OR, NOT)
- Multi-field ordering
- Nested relationship resolvers (with associations enabled)
pip install basiliskFor GraphQL support:
pip install "basilisk[graphql]"from fastapi import FastAPI, Depends
from sqlalchemy import create_engine, Column, Integer, String
from sqlalchemy.orm import declarative_base, Session, sessionmaker
from pydantic import BaseModel
from basilisk import CRUDRouter
# SQLAlchemy Model
Base = declarative_base()
class Product(Base):
__tablename__ = "products"
id = Column(Integer, primary_key=True)
name = Column(String, nullable=False)
price = Column(Integer, nullable=False)
category = Column(String, nullable=False)
# Pydantic Schemas
class ProductCreate(BaseModel):
name: str
price: int
category: str
class ProductResponse(BaseModel):
id: int
name: str
price: int
category: str
model_config = {"from_attributes": True}
# Database Setup
engine = create_engine("sqlite:///./products.db")
Base.metadata.create_all(bind=engine)
SessionLocal = sessionmaker(bind=engine)
def get_db():
db = SessionLocal()
try:
yield db
finally:
db.close()
# Create FastAPI App with Auto-Generated Routes
app = FastAPI()
router = CRUDRouter(
model=Product,
create_schema=ProductCreate,
response_schema=ProductResponse,
get_db=get_db,
)
app.include_router(router, prefix="/products", tags=["products"])Generated Endpoints:
GET /products/- List all products with filteringGET /products/{id}- Get single productPOST /products/- Create productPUT /products/{id}- Update productDELETE /products/{id}- Delete product
# Filter by multiple values (OR within field, AND across fields)
GET /products/?category=electronics,books&status=active
# Select specific fields with aliases
GET /products/?select=id,name,price;product_price
# Aggregations with grouping
GET /products/?select=category,sum(price) as total&groupBy=category
# Multi-field ordering
GET /products/?orderBy=category:asc,price:desc
# Combine all features
GET /products/?category=electronics&select=name,price&orderBy=price:descfrom basilisk import GraphQLCRUDRouter
graphql_router = GraphQLCRUDRouter(
model=Product,
create_schema=ProductCreate,
response_schema=ProductResponse,
get_db=get_db,
)
app.include_router(graphql_router, prefix="/graphql", tags=["graphql"])GraphQL Examples:
# List with advanced filtering
query {
listProducts(
where: {
AND: [
{ category: { eq: "electronics" } }
{ price: { gte: 100 } }
]
}
orderBy: [{ field: "price", direction: DESC }]
) {
id
name
price
}
}
# Relay-style pagination
query {
productConnection(
first: 10
after: "cursor123"
where: { category: { in: ["electronics", "books"] } }
) {
edges {
node {
id
name
price
}
cursor
}
pageInfo {
hasNextPage
endCursor
}
totalCount
}
}
# Create mutation
mutation {
createProduct(input: {
name: "Laptop"
price: 999
category: "electronics"
}) {
id
name
price
}
}from sqlalchemy import ForeignKey
from sqlalchemy.orm import relationship
class User(Base):
__tablename__ = "users"
id = Column(Integer, primary_key=True)
name = Column(String, nullable=False)
posts = relationship("Post", back_populates="author")
class Post(Base):
__tablename__ = "posts"
id = Column(Integer, primary_key=True)
title = Column(String, nullable=False)
author_id = Column(Integer, ForeignKey("users.id"))
author = relationship("User", back_populates="posts")
# Enable associations (opt-in)
router = CRUDRouter(
model=User,
create_schema=UserCreate,
response_schema=UserResponse,
get_db=get_db,
enable_associations=True, # Enable eager loading
)Usage:
# REST: Include related data
GET /users/1?include=posts
# REST: Include nested relationships
GET /users/?include=posts.comments,profile# GraphQL: Nested queries (automatic with associations enabled)
query {
getUser(id: 1) {
id
name
posts {
id
title
}
}
}from basilisk import CRUDRouter, PermissionConfig
from pydantic import BaseModel
class UserContext(BaseModel):
id: int
roles: list[str]
# Define permissions
permissions = PermissionConfig(
roles={
"admin": ["create", "read", "update", "delete"],
"user": ["read"],
"editor": ["create", "read", "update"],
}
)
# Your authentication dependency
def get_current_user(token: str = Header(...)) -> UserContext:
# Your auth logic here
return UserContext(id=1, roles=["user"])
# Apply permissions to router
router = CRUDRouter(
model=Product,
create_schema=ProductCreate,
response_schema=ProductResponse,
get_db=get_db,
permissions=permissions,
get_current_user=get_current_user,
)Result: Users can only access operations allowed by their role. Unauthorized attempts return 403 Forbidden.
# Enable MCP mode for AI agent integration
router = CRUDRouter(
model=Product,
create_schema=ProductCreate,
response_schema=ProductResponse,
get_db=get_db,
enable_mcp=True, # Expose AI-friendly documentation
)MCP Endpoints:
GET /products/.mcp/overview- Complete API overviewGET /products/.mcp/schema- Detailed schema informationGET /products/.mcp/examples- Usage examples for all operationsGET /products/.mcp/capabilities- Supported featuresGET /products/.mcp/guide- Best practices for agents
See the examples/ directory for complete working examples:
basic_example.py- Simple REST APIgraphql_example.py- Basic GraphQL APIcombined_example.py- REST + GraphQL togetheradvanced_query_example.py- Advanced filtering and aggregationadvanced_graphql_example.py- GraphQL with filtering and paginationrbac_example.py- Role-based access controlassociations_example.py- Multi-model relationshipsmcp_mode_example.py- AI agent integration
Run any example and visit:
- REST: http://127.0.0.1:8000/docs (Swagger UI)
- GraphQL: http://127.0.0.1:8000/graphql (GraphQL Playground)
Basilisk includes built-in security features:
- SQL Injection Prevention - Parameterized queries via SQLAlchemy
- Input Validation - Column name whitelisting against model attributes
- Sanitization - Alphanumeric validation for aliases and function names
- Depth Limiting - Max nesting depth for associations (prevents circular refs)
- Python 3.8+
- FastAPI
- SQLAlchemy 2.0+
- Pydantic 2.0+
- Ariadne (optional, for GraphQL)
MIT
Contributions welcome! Please check existing issues or create a new one to discuss changes.
- Async database operations
- Field-level permissions
- DataLoader integration for GraphQL
- Caching strategies
- Rate limiting
- Webhooks