Skip to content

Commit 80aeb00

Browse files
committed
docs: enhance README with detailed quick start guide and feature descriptions
1 parent e3fbfe6 commit 80aeb00

File tree

8 files changed

+158
-169
lines changed

8 files changed

+158
-169
lines changed

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,13 @@
11
# Changelog
22

3+
4+
## 0.11.0
5+
6+
- Update README.md to emphasize rapid development of minimum viable systems
7+
- Add 30-Second Quick Start section for quick onboarding
8+
- Embed GraphiQL HTML template into the library
9+
- Add `get_graphiql_html()` method to `GraphQLHandler` with configurable `endpoint` parameter
10+
311
## 0.10.0
412

513
- add `allow_mutation` option to `create_mcp_server` to enable mutation support in the generated GraphQL server. This allows clients to perform create, update, and delete operations on the data models defined in the SQLModel schema.

README.md

Lines changed: 66 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,21 +5,75 @@
55
[![PyPI Downloads](https://static.pepy.tech/badge/sqlmodel-graphql/month)](https://pepy.tech/projects/sqlmodel-graphql)
66
![Python Versions](https://img.shields.io/pypi/pyversions/sqlmodel-graphql)
77

8-
**Generate GraphQL APIs & MCP from SQLModel — zero configuration required**
8+
**From SQLModel to Running GraphQL API + MCP Server in Minutes**
9+
10+
sqlmodel-graphql is the fastest way to build a minimum viable system:
11+
12+
- **Zero Config GraphQL** - SQLModel classes → GraphQL schema automatically
13+
- **@query/@mutation Decorators** - Mark methods, get endpoints instantly
14+
- **GraphiQL Built-in** - Interactive debugging playground
15+
- **One-Line MCP Server** - Expose APIs to AI assistants
16+
- **Auto N+1 Prevention** - Query optimization handled for you
917

1018
No schema files. No resolvers. No boilerplate.
19+
Just add decorators to your SQLModel classes.
20+
21+
## 30-Second Quick Start
22+
23+
```python
24+
from fastapi import FastAPI
25+
from fastapi.responses import HTMLResponse
26+
from pydantic import BaseModel
27+
from sqlmodel import SQLModel, Field, select
28+
from sqlmodel_graphql import query, GraphQLHandler
29+
30+
# 1. Define your model with @query decorator
31+
class User(SQLModel, table=True):
32+
id: int | None = Field(default=None, primary_key=True)
33+
name: str
34+
35+
@query
36+
async def get_all(cls) -> list['User']:
37+
async with get_session() as session:
38+
return (await session.exec(select(cls))).all()
1139

12-
Just decorators and Python. Your SQLModel classes become GraphQL APIs instantly.
40+
# 2. Create GraphQL handler (auto-generates schema)
41+
handler = GraphQLHandler(base=SQLModel)
1342

14-
Plus: expose your GraphQL via MCP to AI assistants (Claude, GPT, etc.) with **three-layer progressive disclosure** — AI discovers what's available, understands the schema, then executes queries efficiently.
43+
# 3. Setup FastAPI endpoints
44+
class GraphQLRequest(BaseModel):
45+
query: str
46+
variables: dict | None = None
47+
48+
app = FastAPI()
49+
50+
@app.get("/graphql", response_class=HTMLResponse)
51+
async def graphiql():
52+
return handler.get_graphiql_html()
53+
54+
@app.post("/graphql")
55+
async def graphql(req: GraphQLRequest):
56+
return await handler.execute(req.query, req.variables)
57+
```
58+
59+
Run: `uvicorn app:app` and visit `http://localhost:8000/graphql` for the interactive playground.
1560

1661
## Features
1762

18-
- **Automatic SDL Generation**: Generate GraphQL schema from SQLModel classes
19-
- **@query/@mutation Decorators**: Mark methods as GraphQL operations
20-
- **Query Optimization**: Parse GraphQL queries to generate optimized SQLAlchemy queries
21-
- **N+1 Prevention**: Automatic `selectinload` and `load_only` generation
22-
- **MCP Integration**: Expose GraphQL as MCP tools with progressive disclosure for minimal context usage
63+
### Rapid Development
64+
- **Zero Config GraphQL** - SQLModel classes become GraphQL schema automatically
65+
- **@query/@mutation Decorators** - Mark methods as GraphQL operations
66+
- **GraphiQL Integration** - Built-in playground for testing and debugging
67+
68+
### Smart Optimization
69+
- **Auto N+1 Prevention** - `selectinload` and `load_only` generated automatically
70+
- **Query-Aware Loading** - Only fetch requested fields and relationships
71+
- **QueryMeta Injection** - Framework analyzes queries and optimizes database calls
72+
73+
### AI-Ready with MCP
74+
- **One-Line MCP Server** - `config_simple_mcp_server(base=BaseEntity)`
75+
- **Progressive Disclosure** - AI discovers schema, understands, then queries
76+
- **Multi-App Support** - Serve multiple databases through one MCP server
2377

2478
## Installation
2579

@@ -439,6 +493,10 @@ result = await handler.execute("{ users { id name } }")
439493

440494
# Get SDL
441495
sdl = handler.get_sdl()
496+
497+
# Get GraphiQL HTML (for interactive playground)
498+
html = handler.get_graphiql_html() # defaults to /graphql endpoint
499+
html = handler.get_graphiql_html(endpoint="/api/graphql") # custom endpoint
442500
```
443501

444502
**Auto-Discovery Features:**

demo/app.py

Lines changed: 1 addition & 158 deletions
Original file line numberDiff line numberDiff line change
@@ -15,163 +15,6 @@
1515
from demo.models import BaseEntity
1616
from sqlmodel_graphql import GraphQLHandler
1717

18-
# GraphiQL HTML (loaded via CDN)
19-
GRAPHIQL_HTML = """
20-
<!DOCTYPE html>
21-
<html lang="en">
22-
<head>
23-
<meta charset="utf-8" />
24-
<meta name="viewport" content="width=device-width, initial-scale=1" />
25-
<title>GraphiQL - SQLModel GraphQL Demo</title>
26-
<style>
27-
body { margin: 0; }
28-
#graphiql { height: 100dvh; }
29-
.loading {
30-
height: 100%;
31-
display: flex;
32-
align-items: center;
33-
justify-content: center;
34-
font-size: 2rem;
35-
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
36-
}
37-
</style>
38-
<link rel="stylesheet" href="https://esm.sh/graphiql/dist/style.css" />
39-
<link rel="stylesheet" href="https://esm.sh/@graphiql/plugin-explorer/dist/style.css" />
40-
<script type="importmap">
41-
{
42-
"imports": {
43-
"react": "https://esm.sh/react@19.1.0",
44-
"react/jsx-runtime": "https://esm.sh/react@19.1.0/jsx-runtime",
45-
"react-dom": "https://esm.sh/react-dom@19.1.0",
46-
"react-dom/client": "https://esm.sh/react-dom@19.1.0/client",
47-
"@emotion/is-prop-valid": "data:text/javascript,",
48-
"graphiql": "https://esm.sh/graphiql?standalone&external=react,react-dom,@graphiql/react,graphql",
49-
"graphiql/": "https://esm.sh/graphiql/",
50-
"@graphiql/plugin-explorer": "https://esm.sh/@graphiql/plugin-explorer?standalone&external=react,@graphiql/react,graphql",
51-
"@graphiql/react": "https://esm.sh/@graphiql/react?standalone&external=react,react-dom,graphql,@emotion/is-prop-valid",
52-
"@graphiql/toolkit": "https://esm.sh/@graphiql/toolkit?standalone&external=graphql",
53-
"graphql": "https://esm.sh/graphql@16.11.0"
54-
}
55-
}
56-
</script>
57-
</head>
58-
<body>
59-
<div id="graphiql">
60-
<div class="loading">Loading GraphiQL...</div>
61-
</div>
62-
<script type="module">
63-
import React from 'react';
64-
import ReactDOM from 'react-dom/client';
65-
import { GraphiQL, HISTORY_PLUGIN } from 'graphiql';
66-
import { createGraphiQLFetcher } from '@graphiql/toolkit';
67-
import { explorerPlugin } from '@graphiql/plugin-explorer';
68-
69-
const fetcher = createGraphiQLFetcher({ url: '/graphql' });
70-
const plugins = [HISTORY_PLUGIN, explorerPlugin()];
71-
72-
function App() {
73-
return React.createElement(GraphiQL, {
74-
fetcher: fetcher,
75-
plugins: plugins,
76-
defaultQuery: `# Welcome to SQLModel GraphQL Demo!
77-
#
78-
# Try these queries:
79-
80-
# Get all users
81-
query GetUsers {
82-
users(limit: 10) {
83-
id
84-
name
85-
email
86-
}
87-
}
88-
89-
# Get users with their posts (relationship)
90-
query GetUsersWithPosts {
91-
users(limit: 5) {
92-
id
93-
name
94-
email
95-
posts {
96-
id
97-
title
98-
content
99-
}
100-
}
101-
}
102-
103-
# Get a specific user by ID
104-
query GetUser {
105-
user(id: 1) {
106-
id
107-
name
108-
email
109-
}
110-
}
111-
112-
# Get all posts
113-
query GetPosts {
114-
posts(limit: 10) {
115-
id
116-
title
117-
content
118-
author_id
119-
}
120-
}
121-
122-
# Get posts with their authors (relationship)
123-
query GetPostsWithAuthors {
124-
posts(limit: 5) {
125-
id
126-
title
127-
content
128-
author {
129-
id
130-
name
131-
email
132-
}
133-
}
134-
}
135-
136-
# Get posts by author
137-
query GetPostsByAuthor {
138-
posts_by_author(author_id: 1, limit: 10) {
139-
id
140-
title
141-
content
142-
}
143-
}
144-
145-
# Create a new user
146-
mutation CreateUser {
147-
create_user(name: "Charlie", email: "charlie@example.com") {
148-
id
149-
name
150-
email
151-
}
152-
}
153-
154-
# Create a new post
155-
mutation CreatePost {
156-
create_post(title: "My New Post", content: "Hello GraphQL!", author_id: 1) {
157-
id
158-
title
159-
content
160-
author_id
161-
}
162-
}
163-
`
164-
});
165-
}
166-
167-
const container = document.getElementById('graphiql');
168-
const root = ReactDOM.createRoot(container);
169-
root.render(React.createElement(App));
170-
</script>
171-
</body>
172-
</html>
173-
"""
174-
17518

17619
class GraphQLRequest(BaseModel):
17720
"""GraphQL request model."""
@@ -213,7 +56,7 @@ async def lifespan(app: FastAPI):
21356
@app.get("/graphql", response_class=HTMLResponse)
21457
async def graphiql_playground():
21558
"""GraphiQL interactive query interface."""
216-
return GRAPHIQL_HTML
59+
return handler.get_graphiql_html()
21760

21861

21962
@app.post("/graphql")

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "sqlmodel-graphql"
3-
version = "0.10.0"
3+
version = "0.11.0"
44
description = "GraphQL SDL generation and query optimization for SQLModel"
55
authors = [{ name = "tangkikodo", email = "allmonday@126.com" }]
66
readme = "README.md"

src/sqlmodel_graphql/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ async def get_all(cls, query_meta: QueryMeta = None) -> list['User']:
3131

3232
from __future__ import annotations
3333

34-
__version__ = "0.1.0"
34+
__version__ = "0.11.0"
3535

3636
from sqlmodel_graphql.decorator import mutation, query
3737
from sqlmodel_graphql.handler import GraphQLHandler

src/sqlmodel_graphql/graphiql.py

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
"""GraphiQL HTML template for SQLModel GraphQL."""
2+
3+
GRAPHIQL_HTML = """
4+
<!DOCTYPE html>
5+
<html lang="en">
6+
<head>
7+
<meta charset="utf-8" />
8+
<meta name="viewport" content="width=device-width, initial-scale=1" />
9+
<title>GraphiQL - SQLModel GraphQL</title>
10+
<style>
11+
body { margin: 0; }
12+
#graphiql { height: 100dvh; }
13+
.loading {
14+
height: 100%;
15+
display: flex;
16+
align-items: center;
17+
justify-content: center;
18+
font-size: 2rem;
19+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
20+
}
21+
</style>
22+
<link rel="stylesheet" href="https://esm.sh/graphiql/dist/style.css" />
23+
<link rel="stylesheet" href="https://esm.sh/@graphiql/plugin-explorer/dist/style.css" />
24+
<script type="importmap">
25+
{
26+
"imports": {
27+
"react": "https://esm.sh/react@19.1.0",
28+
"react/jsx-runtime": "https://esm.sh/react@19.1.0/jsx-runtime",
29+
"react-dom": "https://esm.sh/react-dom@19.1.0",
30+
"react-dom/client": "https://esm.sh/react-dom@19.1.0/client",
31+
"@emotion/is-prop-valid": "data:text/javascript,",
32+
"graphiql": "https://esm.sh/graphiql?standalone&external=react,react-dom,@graphiql/react,graphql",
33+
"graphiql/": "https://esm.sh/graphiql/",
34+
"@graphiql/plugin-explorer": "https://esm.sh/@graphiql/plugin-explorer?standalone&external=react,@graphiql/react,graphql",
35+
"@graphiql/react": "https://esm.sh/@graphiql/react?standalone&external=react,react-dom,graphql,@emotion/is-prop-valid",
36+
"@graphiql/toolkit": "https://esm.sh/@graphiql/toolkit?standalone&external=graphql",
37+
"graphql": "https://esm.sh/graphql@16.11.0"
38+
}
39+
}
40+
</script>
41+
</head>
42+
<body>
43+
<div id="graphiql">
44+
<div class="loading">Loading GraphiQL...</div>
45+
</div>
46+
<script type="module">
47+
import React from 'react';
48+
import ReactDOM from 'react-dom/client';
49+
import { GraphiQL } from 'graphiql';
50+
import { createGraphiQLFetcher } from '@graphiql/toolkit';
51+
import { explorerPlugin } from '@graphiql/plugin-explorer';
52+
53+
const fetcher = createGraphiQLFetcher({ url: '{graphql_url}' });
54+
55+
function App() {
56+
return React.createElement(GraphiQL, {
57+
fetcher: fetcher,
58+
plugins: [explorerPlugin()]
59+
});
60+
}
61+
62+
const container = document.getElementById('graphiql');
63+
const root = ReactDOM.createRoot(container);
64+
root.render(React.createElement(App));
65+
</script>
66+
</body>
67+
</html>
68+
"""

src/sqlmodel_graphql/handler.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
from typing import TYPE_CHECKING, Any
66

77
from sqlmodel_graphql.discovery import EntityDiscovery
8+
from sqlmodel_graphql.graphiql import GRAPHIQL_HTML
89
from sqlmodel_graphql.introspection import IntrospectionGenerator
910
from sqlmodel_graphql.query_parser import QueryParser
1011
from sqlmodel_graphql.scanning import MethodScanner
@@ -93,6 +94,17 @@ def get_sdl(self) -> str:
9394
"""
9495
return self._sdl_generator.generate()
9596

97+
def get_graphiql_html(self, endpoint: str = "/graphql") -> str:
98+
"""Get the GraphiQL HTML template.
99+
100+
Args:
101+
endpoint: GraphQL API endpoint URL. Defaults to "/graphql".
102+
103+
Returns:
104+
HTML string for GraphiQL playground.
105+
"""
106+
return GRAPHIQL_HTML.replace("{graphql_url}", endpoint)
107+
96108
async def execute(
97109
self,
98110
query: str,

0 commit comments

Comments
 (0)