GraphQL SDL generation and query optimization for SQLModel
Project description
SQLModel GraphQL
The Simplest Way to GraphQL -- Enable Your SQLModel Application
No schema files. No resolvers. No boilerplate. Just decorators and Python. Your SQLModel classes become GraphQL APIs instantly.
Plus: Built-in MCP Server — Expose your data to AI assistants (Claude, GPT, etc.) with zero extra code.
Features
- Automatic SDL Generation: Generate GraphQL schema from SQLModel classes
- @query/@mutation Decorators: Mark methods as GraphQL operations
- Query Optimization: Parse GraphQL queries to generate optimized SQLAlchemy queries
- N+1 Prevention: Automatic
selectinloadandload_onlygeneration - MCP Integration: Expose GraphQL as MCP tools for AI assistants
Installation
pip install sqlmodel-graphql
Or with uv:
uv add sqlmodel-graphql
Quick Start
1. Define Your Models
from typing import Optional
from sqlmodel import SQLModel, Field, Relationship, select
from sqlmodel_graphql import query, mutation, QueryMeta
class User(SQLModel, table=True):
id: Optional[int] = Field(default=None, primary_key=True)
name: str
email: str
posts: list["Post"] = Relationship(back_populates="author")
@query(name='users')
async def get_all(cls, limit: int = 10, query_meta: Optional[QueryMeta] = None) -> list['User']:
"""Get all users with optional query optimization."""
from demo.database import async_session
async with async_session() as session:
stmt = select(cls).limit(limit)
if query_meta:
# Apply optimization: only load requested fields and relationships
stmt = stmt.options(*query_meta.to_options(cls))
result = await session.exec(stmt)
return list(result.all())
@query(name='user')
async def get_by_id(cls, id: int, query_meta: Optional[QueryMeta] = None) -> Optional['User']:
return await fetch_user(id, query_meta)
@mutation(name='createUser')
async def create(cls, name: str, email: str) -> 'User':
return await create_user(name, email)
class Post(SQLModel, table=True):
id: Optional[int] = Field(default=None, primary_key=True)
title: str
content: str = ""
author_id: int = Field(foreign_key="user.id")
author: User = Relationship(back_populates="posts")
2. Generate GraphQL SDL
from sqlmodel_graphql import SDLGenerator
generator = SDLGenerator([User, Post])
sdl = generator.generate()
print(sdl)
Output:
type User {
id: Int
name: String!
email: String!
posts: [Post!]!
}
type Post {
id: Int
title: String!
content: String!
author_id: Int!
author: User!
}
type Query {
users(limit: Int): [User!]!
user(id: Int!): User
}
type Mutation {
createUser(name: String!, email: String!): User!
}
3. Execute Queries with GraphQLHandler
from sqlmodel_graphql import GraphQLHandler
handler = GraphQLHandler(entities=[User, Post])
# Execute a GraphQL query
result = await handler.execute("""
{
users(limit: 5) {
id
name
posts {
title
comments {
content
author {
name
}
}
}
}
}
""")
# Result includes nested relationships automatically:
# {
# "data": {
# "users": [
# {
# "id": 1,
# "name": "Alice",
# "posts": [
# {
# "title": "Hello World",
# "comments": [
# {"content": "Great post!", "author": {"name": "Bob"}}
# ]
# }
# ]
# }
# ]
# }
# }
MCP Integration
Turn your SQLModel entities into AI-ready tools with a single function call.
from sqlmodel_graphql.mcp import create_mcp_server
from myapp.models import BaseEntity
# Create MCP server from your base class
# All SQLModel subclasses with @query/@mutation decorators are auto-discovered
mcp = create_mcp_server(
base=BaseEntity,
name="My Blog API"
)
# Run for AI assistants (Claude Desktop, etc.)
mcp.run() # stdio mode (default)
# mcp.run(transport="streamable-http") # HTTP mode
Available MCP Tools
The server exposes three tools for AI:
- get_schema - Discover available queries, mutations, and types
- graphql_query - Execute dynamic GraphQL queries with dot-notation field paths
- graphql_mutation - Execute GraphQL mutations
Example: AI Query Flow
AI: What data is available?
→ get_schema() → Returns queries, mutations, types
AI: Get users with their posts
→ graphql_query(
operation_name="users",
arguments={"limit": 10},
fields=["id", "name", "posts.title", "posts.content"]
)
AI: Create a new user
→ graphql_mutation(
operation_name="create_user",
arguments={"name": "Alice", "email": "alice@example.com"},
fields=["id", "name"]
)
Why MCP?
Traditional GraphQL requires AI to:
- Know the exact GraphQL syntax
- Understand the full schema structure
With MCP, AI can:
- Discover schema dynamically via
get_schema - Query with simple field paths (no GraphQL syntax needed)
- Focus on business logic, not query construction
Installation
# Core library
pip install sqlmodel-graphql
# MCP support (optional)
pip install mcp
Running MCP Server
# demo/mcp_server.py
uv run python --with mcp demo/mcp_server.py # stdio mode
uv run python --with mcp demo/mcp_server.py --http # HTTP mode
How It Works
GraphQL Query QueryMeta
───────────── ─────────
{ users { QueryMeta(
id fields=[FieldSelection('id'), FieldSelection('name')],
name relationships={
posts { 'posts': RelationshipSelection(
title fields=[FieldSelection('title')]
} }
} }
)
↓
query_meta.to_options(User)
↓
select(User).options(
load_only(User.id, User.name),
selectinload(User.posts).options(load_only(Post.title))
)
Query Optimization Flow
- GraphQLHandler receives the query
- QueryParser parses the selection set into QueryMeta
- QueryMeta is injected into your
@querymethod asquery_metaparameter - query_meta.to_options(entity) generates SQLAlchemy options:
load_only()for requested scalar fieldsselectinload()for requested relationships
- Database query only fetches what's needed, preventing N+1 problems
API Reference
@query(name=None, description=None)
Mark a method as a GraphQL query.
@query(name='users', description='Get all users')
async def get_all(cls, limit: int = 10, query_meta: Optional[QueryMeta] = None) -> list['User']:
...
@mutation(name=None, description=None)
Mark a method as a GraphQL mutation.
@mutation(name='createUser')
async def create(cls, name: str, email: str) -> 'User':
...
SDLGenerator(entities)
Generate GraphQL SDL from SQLModel classes.
generator = SDLGenerator([User, Post])
sdl = generator.generate()
GraphQLHandler(entities=None, base=None)
Execute GraphQL queries against SQLModel entities with auto-discovery support.
# Auto-discover from SQLModel (default)
handler = GraphQLHandler()
# Use custom base class
handler = GraphQLHandler(base=MyBase)
# Explicit entities
handler = GraphQLHandler(entities=[User, Post])
result = await handler.execute("{ users { id name } }")
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
QueryParser()
Parse GraphQL queries to QueryMeta.
parser = QueryParser()
metas = parser.parse("{ users { id name } }")
# metas['users'] -> QueryMeta(fields=[...], relationships={...})
QueryMeta
Metadata extracted from GraphQL selection set.
@dataclass
class QueryMeta:
fields: list[FieldSelection]
relationships: dict[str, RelationshipSelection]
def to_options(self, entity: type[SQLModel]) -> list[Any]:
"""Convert to SQLAlchemy options for query optimization."""
License
MIT License
Project details
Release history Release notifications | RSS feed
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 sqlmodel_graphql-0.3.0.tar.gz.
File metadata
- Download URL: sqlmodel_graphql-0.3.0.tar.gz
- Upload date:
- Size: 124.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.10.9 {"installer":{"name":"uv","version":"0.10.9","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
bf4bad71bace74a1241aecdd96fa8024501456673de5383def60e28eaadaa3ec
|
|
| MD5 |
41c771c1541933746e5b9a87d51e5356
|
|
| BLAKE2b-256 |
e4fb21efa9dd8bb9209035953eb330062152270e561dd5d077b6e1d45bdcd32c
|
File details
Details for the file sqlmodel_graphql-0.3.0-py3-none-any.whl.
File metadata
- Download URL: sqlmodel_graphql-0.3.0-py3-none-any.whl
- Upload date:
- Size: 44.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.10.9 {"installer":{"name":"uv","version":"0.10.9","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ded92f00c2c5a9409c3dd2f85eb1a571ba073d449f5a032b1283c4b7e440dcc9
|
|
| MD5 |
79bf3a9411b3d01349b6f8d074cbb939
|
|
| BLAKE2b-256 |
54db720b06a490f5fe9a7a4f38473f757400d79e7177aec3d025397f9fc97a8d
|