SQLModel GraphQL
From SQLModel to Running GraphQL API + MCP Server in Minutes
sqlmodel-graphql is the fastest way to build a minimum viable system:
- Zero Config GraphQL - SQLModel classes → GraphQL schema automatically
- @query/@mutation Decorators - Mark methods, get endpoints instantly
- GraphiQL Built-in - Interactive debugging playground
- One-Line MCP Server - Expose APIs to AI assistants
- Auto N+1 Prevention - DataLoader batch loading for relationships
No schema files. No resolvers. No boilerplate. Just add decorators to your SQLModel classes.
30-Second Quick Start
from fastapi import FastAPI
from fastapi.responses import HTMLResponse
from pydantic import BaseModel
from sqlmodel import SQLModel, Field, select
from sqlmodel_graphql import query, GraphQLHandler
# 1. Define your model with @query decorator
class User(SQLModel, table=True):
id: int | None = Field(default=None, primary_key=True)
name: str
@query
async def get_all(cls) -> list['User']:
async with get_session() as session:
return (await session.exec(select(cls))).all()
# 2. Create GraphQL handler (auto-generates schema)
handler = GraphQLHandler(base=SQLModel)
# 3. Setup FastAPI endpoints
class GraphQLRequest(BaseModel):
query: str
app = FastAPI()
@app.get("/graphql", response_class=HTMLResponse)
async def graphiql():
return handler.get_graphiql_html()
@app.post("/graphql")
async def graphql(req: GraphQLRequest):
return await handler.execute(req.query)
Run: uvicorn app:app and visit http://localhost:8000/graphql for the interactive playground.
Features
Rapid Development
- Zero Config GraphQL - SQLModel classes become GraphQL schema automatically
- @query/@mutation Decorators - Mark methods as GraphQL operations
- GraphiQL Integration - Built-in playground for testing and debugging
Smart Optimization
- Auto N+1 Prevention - DataLoader batch loading for all relationships
- Level-by-Level Resolution - Relationships loaded in batches per depth level
- Paginated Relationships - Built-in cursor-free pagination for list relationships
AI-Ready with MCP
- One-Line MCP Server -
config_simple_mcp_server(base=BaseEntity) - Progressive Disclosure - AI discovers schema, understands, then queries
- Multi-App Support - Serve multiple databases through one MCP server
Installation
pip install sqlmodel-graphql
Or with uv:
uv add sqlmodel-graphql
uv add sqlmodel-graphql[fastmcp] # include mcp server
Demo
running GraphQL demo:
uv run python -m demo.app
# and visit localhost:8000/graphql
running MCP demo
uv run --with fastmcp python -m demo.mcp_server # stdio mode
uv run --with fastmcp python -m demo.mcp_server --http # http mode
Quick Start
1. Define Your Models
from typing import Optional
from sqlmodel import SQLModel, Field, Relationship, select
from sqlmodel_graphql import query, mutation
class BaseEntity(SQLModel):
"""Base class for all entities."""
pass
class User(BaseEntity, table=True):
id: Optional[int] = Field(default=None, primary_key=True)
name: str
email: str
posts: list["Post"] = Relationship(
back_populates="author",
sa_relationship_kwargs={"order_by": "Post.id"},
)
@query
async def get_all(cls, limit: int = 10) -> list['User']:
"""Get all users."""
async with get_session() as session:
stmt = select(cls).limit(limit)
result = await session.exec(stmt)
return list(result.all())
# Generates GraphQL field: userGetAll(limit: Int): [User!]!
@query
async def get_by_id(cls, id: int) -> Optional['User']:
"""Get a user by ID."""
async with get_session() as session:
stmt = select(cls).where(cls.id == id)
result = await session.exec(stmt)
return result.first()
# Generates GraphQL field: userGetById(id: Int!): User
@mutation
async def create(cls, name: str, email: str) -> 'User':
"""Create a new user."""
async with get_session() as session:
user = cls(name=name, email=email)
session.add(user)
await session.commit()
await session.refresh(user)
return user
# Generates GraphQL field: userCreate(name: String!, email: String!): User!
class Post(BaseEntity, table=True):
id: Optional[int] = Field(default=None, primary_key=True)
title: str
content: str = ""
author_id: int = Field(foreign_key="user.id")
author: Optional[User] = Relationship(back_populates="posts")
Note on relationships: For list relationships, add sa_relationship_kwargs={"order_by": "Entity.column"} to enable pagination support.
2. Create Handler (Auto-generates SDL)
from sqlmodel_graphql import GraphQLHandler
# With pagination support:
handler = GraphQLHandler(
base=BaseEntity,
session_factory=async_session,
enable_pagination=True,
)
# Get the SDL if needed
sdl = handler.get_sdl()
print(sdl)
3. Execute Queries
Try it live: Run
uv run uvicorn demo.app:app --reloadto start a FastAPI server with GraphiQL at http://localhost:8000/graphql
from sqlmodel_graphql import GraphQLHandler
handler = GraphQLHandler(
base=BaseEntity,
session_factory=async_session,
enable_pagination=True,
)
# Relationships are resolved via DataLoader (no N+1)
result = await handler.execute("""
{
userGetAll(limit: 5) {
id
name
posts(limit: 3) {
items {
title
author { name }
}
pagination { has_more total_count }
}
}
}
""")
How Relationship Resolution Works
Relationships are resolved automatically via DataLoader — no manual eager loading needed.
Execution flow:
- Root
@querymethod returns entity instances (scalar fields only) - Framework walks the GraphQL selection tree level-by-level
- At each level, collects FK values and batch-loads relationships via DataLoader
- For paginated lists, uses
ROW_NUMBER()window functions for efficient per-parent pagination
Benefits:
- Automatic N+1 Prevention: All relationships loaded in batched queries
- Pagination Support:
limit/offseton list relationships withhas_moreandtotal_count - Zero Configuration: No
selectinloadorload_onlyneeded in user code
Auto-Generated Standard Queries
Pass AutoQueryConfig to GraphQLHandler to automatically generate by_id and by_filter queries for every entity — no @query decorator needed.
Configuration
from sqlmodel_graphql import GraphQLHandler, AutoQueryConfig
config = AutoQueryConfig(
session_factory=async_session, # Required: async session factory
default_limit=10, # Default limit for by_filter
generate_by_id=True, # Generate by_id query
generate_by_filter=True, # Generate by_filter query
)
handler = GraphQLHandler(
base=BaseEntity,
auto_query_config=config,
session_factory=async_session,
enable_pagination=True,
)
When auto_query_config is provided, the handler discovers all entity subclasses (not only those with decorators) and attaches standard queries.
Generated Queries
For an entity named User with primary key id:
| Method | GraphQL Field | Return Type | Description |
|---|---|---|---|
by_id |
userById(id: Int!): User |
User | None |
Find a single entity by primary key |
by_filter |
userByFilter(filter: UserFilterInput, limit: Int): [User!]! |
list[User] |
Filter entities by field values |
FilterInput is auto-generated per entity. All fields are optional — only non-None values are used as WHERE conditions (exact match).
Example
# Get by ID
{
userById(id: 1) {
id
name
email
posts { title }
}
}
# Filter by fields
{
userByFilter(filter: { name: "Alice" }, limit: 5) {
id
name
email
}
}
# List all (no filter)
{
userByFilter(limit: 20) {
id
name
}
}
Notes:
by_idrequires exactly one primary key field (detected fromidfield orprimary_key=True).by_filtersupports exact match only; for complex queries, write custom@querymethods.- Existing methods on the entity are not overridden.
MCP Integration
Turn your SQLModel entities into AI-ready tools with a single function call.
Simple MCP Server (Single App)
For single-application scenarios with one database:
from sqlmodel_graphql.mcp import config_simple_mcp_server
from myapp.models import BaseEntity
# Create simplified MCP server - only 3 tools, no app_name required
mcp = config_simple_mcp_server(
base=BaseEntity,
name="My Blog API",
desc="Blog system with users and posts"
)
# Run for AI assistants (Claude Desktop, etc.)
mcp.run() # stdio mode (default)
# mcp.run(transport="streamable-http") # HTTP mode
Parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
base |
type |
Required | SQLModel base class. All subclasses with @query/@mutation decorators will be automatically discovered. |
name |
str |
"SQLModel GraphQL API" |
Name of the MCP server (shown in MCP clients). |
desc |
str | None |
None |
Optional description for the GraphQL schema (used for both Query and Mutation type descriptions). |
allow_mutation |
bool |
False |
If True, registers graphql_mutation tool and includes Mutation type in schema. Default is read-only mode. |
Available Tools (3 tools):
| Tool | Description |
|---|---|
get_schema() |
Get the complete GraphQL schema in SDL format |
graphql_query(query) |
Execute GraphQL queries |
graphql_mutation(mutation) |
Execute GraphQL mutations |
Example: AI Query Flow:
AI: What's available?
→ get_schema() → Returns full SDL
AI: Get users with their posts
→ graphql_query(query="{ userGetUsers(limit: 10) { id name posts { title } } }")
AI: Create a new user
→ graphql_mutation(mutation="mutation { userCreate(name: \"Alice\", email: \"alice@example.com\") { id name } }")
Multi-App MCP Server
For scenarios with multiple independent databases:
from sqlmodel_graphql.mcp import create_mcp_server
from myapp.blog_models import BlogBaseEntity
from myapp.shop_models import ShopBaseEntity
apps = [
{
"name": "blog",
"base": BlogBaseEntity,
"description": "Blog system API",
},
{
"name": "shop",
"base": ShopBaseEntity,
"description": "E-commerce system API",
}
]
mcp = create_mcp_server(apps=apps, name="My Multi-App API")
mcp.run()
Parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
apps |
list[AppConfig] |
Required | List of app configurations. Each app has its own GraphQL schema and independent database. |
name |
str |
"Multi-App SQLModel GraphQL API" |
Name of the MCP server (shown in MCP clients). |
allow_mutation |
bool |
False |
If True, registers mutation-related tools (list_mutations, get_mutation_schema, graphql_mutation). Default is read-only mode. |
AppConfig Fields:
| Field | Type | Required | Description |
|---|---|---|---|
name |
str |
Yes | Unique identifier for the application (e.g., "blog", "shop"). |
base |
type[SQLModel] |
Yes | SQLModel base class for this application. |
description |
str | None |
No | Human-readable description of the application. |
query_description |
str | None |
No | Description for the Query type in GraphQL schema. |
mutation_description |
str | None |
No | Description for the Mutation type in GraphQL schema. |
Available Tools (8 tools with app routing):
| Tool | Description |
|---|---|
list_apps() |
List all available applications |
list_queries(app_name) |
List queries for an app |
list_mutations(app_name) |
List mutations for an app |
get_query_schema(name, app_name) |
Get query schema details |
get_mutation_schema(name, app_name) |
Get mutation schema details |
graphql_query(query, app_name) |
Execute GraphQL queries |
graphql_mutation(mutation, app_name) |
Execute GraphQL mutations |
Installation
pip install sqlmodel-graphql[fastmcp]
Running MCP Server
uv run --with fastmcp python -m demo.mcp_server # stdio mode
uv run --with fastmcp python -m demo.mcp_server --http # http mode
API Reference
@query
Mark a method as a GraphQL query. The field name is auto-generated as {entityName}{MethodName} in camelCase.
@query
async def get_all(cls, limit: int = 10) -> list['User']:
"""Get all users.""" # Docstring becomes the field description
...
# Generates: userGetAll(limit: Int): [User!]!
@mutation
Mark a method as a GraphQL mutation. The field name is auto-generated as {entityName}{MethodName} in camelCase.
@mutation
async def create(cls, name: str, email: str) -> 'User':
"""Create a new user."""
...
# Generates: userCreate(name: String!, email: String!): User!
GraphQLHandler
Execute GraphQL queries against SQLModel entities with auto-discovery.
# Recommended: Use base class with session_factory
handler = GraphQLHandler(
base=BaseEntity,
session_factory=async_session,
enable_pagination=True,
)
# Execute queries
result = await handler.execute("{ userGetAll { id name posts { items { title } } } }")
# Get SDL
sdl = handler.get_sdl()
# Get GraphiQL HTML (for interactive playground)
html = handler.get_graphiql_html() # defaults to /graphql endpoint
html = handler.get_graphiql_html(endpoint="/api/graphql") # custom endpoint
Parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
base |
type |
Required | SQLModel base class for entity discovery. |
session_factory |
Callable | None |
None |
Async session factory for DataLoader queries. Required for relationship resolution. |
auto_query_config |
AutoQueryConfig | None |
None |
Auto-generate by_id/by_filter queries. |
enable_pagination |
bool |
False |
Enable pagination for list relationships. |
query_description |
str | None |
None |
Custom description for Query type. |
mutation_description |
str | None |
None |
Custom description for Mutation type. |
Auto-Discovery Features:
- Automatically finds all SQLModel subclasses with
@query/@mutationdecorators - Includes all related entities through Relationship fields
- Supports custom base classes for better organization
- Recursive discovery of nested relationships
License
MIT License
Metadata
Release files for sqlmodel-graphql 0.14.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| sqlmodel_graphql-0.14.0.tar.gz | 213.8 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| sqlmodel_graphql-0.14.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 290.3 kB
Release files / sqlmodel_graphql-0.14.0.tar.gz
| Download URL | sqlmodel_graphql-0.14.0.tar.gz |
|---|---|
| Size | 213.8 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
c90d8a59786406e33e02803ce8c37dace4cb857c3b196fc6319b35f1df8bbb63
|
|
BLAKE2b-256 checksum How to use checksums |
18f3f7fc24c427890338f73e6fe8738ba9c7b489f643a6237e41d75104c95a79
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.8.13
|
Release files / sqlmodel_graphql-0.14.0-py3-none-any.whl
| Download URL | sqlmodel_graphql-0.14.0-py3-none-any.whl |
|---|---|
| Size | 76.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
ea9df2d7653ff465bb537f5f41c2f7d8b7e3b0bb67cbd90ce6e44e92053ecf41
|
|
BLAKE2b-256 checksum How to use checksums |
30928f9c8c5f7a430a6f9c7fc88843f375b6d07b430863026ae145fe66babe18
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.8.13
|