diff --git a/CLAUDE.md b/CLAUDE.md
index afe75ae..41dab9f 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -2,9 +2,14 @@
## 项目定位
-sqlmodel-nexus:从 SQLModel 类自动生成 GraphQL API,并提供 Core API 模式构建用例响应的 Python 库。
-- **GraphQL 模式**:SDL 自动生成 + DataLoader 批量关系加载 + MCP 服务集成
-- **Core API 模式**:DefineSubset DTO + ErManager + Resolver 模型驱动的响应构建
+sqlmodel-nexus:从 SQLModel 实体模型出发,自动生成 GraphQL / REST / MCP 三种 API 的 Python 库。
+
+核心理念:定义一次数据模型,获得三种 API 输出,N+1 查询自动防护。
+
+三条使用路径:
+- **GraphQL 模式**:在实体上写 `@query`/`@mutation` → 自动生成 SDL → GraphQLHandler 执行查询
+- **Core API 模式**:`DefineSubset` DTO → `ErManager` + `Resolver` → 构建 REST 响应 / 业务 DTO
+- **UseCase 模式**:`UseCaseService` 业务类 → 同时输出 MCP(AI Agent)和 FastAPI(REST)
## 技术栈
diff --git a/docs/advanced/mcp_service.md b/docs/advanced/mcp_service.md
index b5f4c3d..b5f04aa 100644
--- a/docs/advanced/mcp_service.md
+++ b/docs/advanced/mcp_service.md
@@ -2,6 +2,10 @@
Expose SQLModel APIs to AI agents — create an MCP service with a single line of code.
+> **Prerequisites**: SQLModel entities with `@query`/`@mutation` methods and a `session_factory`. This path covers the **GraphQL→MCP** bridge — for the **UseCase→MCP** path see [UseCase Service](./use_case_service.md).
+>
+> **Live demo**: [`demo/mcp_server_simple.py`](https://github.com/allmonday/sqlmodel-nexus/blob/master/demo/mcp_server_simple.py) (single-app) and [`demo/mcp_server.py`](https://github.com/allmonday/sqlmodel-nexus/blob/master/demo/mcp_server.py) (multi-app).
+
## Installation
```bash
diff --git a/docs/advanced/use_case_fastapi.md b/docs/advanced/use_case_fastapi.md
index 52857a5..2875cf9 100644
--- a/docs/advanced/use_case_fastapi.md
+++ b/docs/advanced/use_case_fastapi.md
@@ -2,6 +2,10 @@
Using the same UseCaseService class in FastAPI — routes are thin wrappers, business logic stays in the service.
+> **Prerequisites**: [UseCase Service](./use_case_service.md). You should have a UseCaseService subclass with `@query`/`@mutation` methods before wiring it into FastAPI.
+>
+> **Prerequisite demo**: [`demo/use_case/mcp_server.py`](https://github.com/allmonday/sqlmodel-nexus/blob/master/demo/use_case/mcp_server.py) — defines the services. [`demo/use_case/fastapi.py`](https://github.com/allmonday/sqlmodel-nexus/blob/master/demo/use_case/fastapi.py) — the same services as REST.
+
## Route Definitions
```python
diff --git a/docs/advanced/use_case_service.md b/docs/advanced/use_case_service.md
index e0934c4..4bdd9aa 100644
--- a/docs/advanced/use_case_service.md
+++ b/docs/advanced/use_case_service.md
@@ -2,6 +2,10 @@
UseCaseService lets you define business logic as service classes that serve both MCP and web frameworks — one definition, two presentations.
+> **Prerequisites**: [Core API Mode](../guide/core_api.md) — ErManager + DefineSubset + Resolver. UseCaseService wraps Core API DTOs into business methods.
+>
+> **Live demo**: See [`demo/use_case/mcp_server.py`](https://github.com/allmonday/sqlmodel-nexus/blob/master/demo/use_case/mcp_server.py) for a working MCP server — and [`demo/use_case/fastapi.py`](https://github.com/allmonday/sqlmodel-nexus/blob/master/demo/use_case/fastapi.py) for the same services exposed as REST.
+
## Design Philosophy
```
diff --git a/docs/advanced/voyager.md b/docs/advanced/voyager.md
index df9716f..dbcc4d7 100644
--- a/docs/advanced/voyager.md
+++ b/docs/advanced/voyager.md
@@ -2,6 +2,8 @@
sqlmodel-nexus includes a built-in Voyager module that provides interactive UseCase service graphs and ER entity relationship visualization.
+> **Prerequisites**: [UseCase Service](./use_case_service.md) (for service graph) and optionally [Core API Mode](../guide/core_api.md) (for ER diagram integration via `er_manager`).
+
## create_use_case_voyager
```python
diff --git a/docs/guide/core_api.md b/docs/guide/core_api.md
index 132d4d4..3001754 100644
--- a/docs/guide/core_api.md
+++ b/docs/guide/core_api.md
@@ -2,7 +2,11 @@
The Core API mode is for scenarios beyond GraphQL — FastAPI REST endpoints, service layer response assembly, or any use-case DTO. Same DataLoader batch loading, same N+1 prevention.
-Core concepts progress in order: **Implicit auto-loading → resolve_\* → post_\* → Cross-layer data flow**.
+> **Prerequisites**: SQLModel entities defined with `Relationship` / `Field(foreign_key=...)`.
+>
+> **Live demo**: See [`demo/core_api/`](https://github.com/allmonday/sqlmodel-nexus/tree/master/demo/core_api) for a complete REST server with Sprint/Task/User models. The DTOs in [`dtos.py`](https://github.com/allmonday/sqlmodel-nexus/blob/master/demo/core_api/dtos.py) progress through 5 levels of complexity.
+
+Core concepts progress in order: **Implicit auto-loading → resolve\_\* → post\_\* → Cross-layer data flow**.
## Step 1: DefineSubset + Implicit Auto-Loading
@@ -24,6 +28,8 @@ class SprintDTO(DefineSubset):
tasks: list[TaskDTO] = [] # Name matches Sprint.tasks relationship → auto-loaded
```
+> ⬆ This is **Level 2** in the demo's [`dtos.py`](https://github.com/allmonday/sqlmodel-nexus/blob/master/demo/core_api/dtos.py#L30).
+
## ErManager Initialization
```python
@@ -48,49 +54,23 @@ app = FastAPI()
async def get_sprints():
async with async_session() as session:
sprints = (await session.exec(select(Sprint))).all()
- dtos = [SprintDTO(id=s.id, name=s.name) for s in sprints]
- return await Resolver().resolve(dtos)
+ dtos = [SprintDTO(**s.model_dump()) for s in sprints]
+ return await Resolver().resolve(dtos) # tasks + owner auto-loaded
```
-## Four Conditions for Implicit Auto-Loading
-
-The Resolver automatically loads relationship fields (no need to write `resolve_*`) when all conditions are met:
-
-1. The field has no corresponding `resolve_*` method
-2. The field is an extra field (not in the `__subset__` definition)
-3. The field name matches a registered ORM/custom relationship
-4. The field type is a BaseModel DTO compatible with the relationship target entity
+> **Tip**: For more efficient field-selective queries, use `build_dto_select(SprintDTO)` — it generates a SQLAlchemy select() that only fetches the fields declared in `__subset__`. See [`demo/core_api/dtos.py` → `TaskService.list_tasks`](https://github.com/allmonday/sqlmodel-nexus/blob/master/demo/use_case/mcp_server.py#L63).
-## DefineSubset Rules
+## How Auto-Loading Works
-- `__subset__` accepts a tuple `(Entity, ('field1', 'field2'))`
-- FK fields (e.g., `owner_id`) are automatically hidden from serialization output, but remain internally accessible in `resolve_*`
-- Relationship fields are declared in the class body (not in `__subset__`), and must use DTO types
-
-## How It Works
-
-```
-SprintDTO(id=1, name="Sprint 1")
- → Implicit auto-load: tasks → [TaskDTO(...), TaskDTO(...)]
- → Each TaskDTO: Implicit auto-load: owner → UserDTO(...)
- → Result: complete nested response tree
-```
+Implicit auto-loading triggers when **all** conditions are true:
-Each relationship executes only one DataLoader query, regardless of how many Sprints or Tasks exist.
-
-## DTO Type Constraint
-
-```python
-# Wrong — direct use of SQLModel entity is prohibited
-class TaskDTO(DefineSubset):
- owner: User | None = None # TypeError!
-
-# Correct — use DTO type
-class TaskDTO(DefineSubset):
- owner: UserDTO | None = None # OK
-```
+1. The field has no corresponding `resolve_*` method
+2. The field is an extra field (not in `__subset__` fields)
+3. The field name matches a registered ORM or custom relationship
+4. The field type is a DefineSubset DTO compatible with the relationship target
## Next Steps
-- [Core API Advanced](./core_api_advanced.md) — resolve_*/post_*/cross-layer data flow
-- [Custom Relationships](./custom_relationship.md) — Non-ORM relationship declarations
+- [Core API Advanced](./core_api_advanced.md) — resolve_*, post_*, cross-layer data flow
+- [Custom Relationships](./custom_relationship.md) — non-ORM relationships
+- [UseCase Service](../advanced/use_case_service.md) — wrap Core API DTOs in business services
diff --git a/docs/guide/core_api_advanced.md b/docs/guide/core_api_advanced.md
index 7b2b1a5..20b224e 100644
--- a/docs/guide/core_api_advanced.md
+++ b/docs/guide/core_api_advanced.md
@@ -2,6 +2,10 @@
When implicit auto-loading is not enough, Core API provides three progressive capabilities: `resolve_*` for custom loading, `post_*` for derived field computation, and cross-layer data flow.
+> **Prerequisites**: [Core API Mode](./core_api.md) — specifically DefineSubset, ErManager, and implicit auto-loading.
+>
+> **Live demo**: The advanced concepts below correspond to Levels 3–5 in [`demo/core_api/dtos.py`](https://github.com/allmonday/sqlmodel-nexus/blob/master/demo/core_api/dtos.py).
+
## resolve_*: Custom Loading
When the field name doesn't match a relationship, or custom logic is needed, use `resolve_*`:
@@ -58,6 +62,8 @@ class SprintDTO(DefineSubset):
return sorted({t.owner.name for t in self.tasks if t.owner})
```
+> ⬆ This is **Level 3** in the demo: [`SprintSummary`](https://github.com/allmonday/sqlmodel-nexus/blob/master/demo/core_api/dtos.py#L53).
+
Execution order:
1. Implicit auto-loading → `tasks` populated with TaskDTO list
@@ -78,60 +84,72 @@ Execution order:
Use when parent and child nodes need cross-layer collaboration. Only necessary when the tree structure truly matters.
-### ExposeAs: Ancestor → Descendant
+**Key tools**:
-```python
-from typing import Annotated
-from sqlmodel_nexus import ExposeAs
+- `ExposeAs`: Parent exposes a value to all descendants via `ancestor_context`
+- `SendTo`: Child sends a value upward to ancestor Collector
+- `Collector`: Aggregates all values sent via SendTo
-class SprintDTO(DefineSubset):
- __subset__ = (Sprint, ("id", "name"))
- name: Annotated[str, ExposeAs('sprint_name')] # Expose to descendants
- tasks: list[TaskDTO] = []
-```
+```python
+from sqlmodel_nexus import Collector, DefineSubset, SubsetConfig
-### SendTo + Collector: Descendant → Ancestor
+class TaskDTO(DefineSubset):
+ __subset__ = SubsetConfig(
+ kls=Task, fields=["id", "title"],
+ send_to=[("owner", "contributors")], # Send owner to ancestor collector
+ )
+ owner: UserDTO | None = None
-```python
-from sqlmodel_nexus import SendTo, Collector
+ def post_full_title(self, ancestor_context=None):
+ sprint_name = (ancestor_context or {}).get("sprint_name", "unknown")
+ return f"{sprint_name} / {self.title}"
class SprintDTO(DefineSubset):
- __subset__ = (Sprint, ("id", "name"))
- name: Annotated[str, ExposeAs('sprint_name')]
+ __subset__ = SubsetConfig(
+ kls=Sprint, fields=["id", "name"],
+ expose_as=[("name", "sprint_name")], # Expose name to descendants
+ )
tasks: list[TaskDTO] = []
contributors: list[UserDTO] = []
- def post_contributors(self, collector=Collector('contributors')):
- return collector.values() # Collect values sent by descendants
-
-class TaskDTO(DefineSubset):
- __subset__ = (Task, ("id", "title", "owner_id"))
- owner: Annotated[UserDTO | None, SendTo('contributors')] = None # Send to ancestor
- full_title: str = ""
-
- def post_full_title(self, ancestor_context):
- return f"{ancestor_context['sprint_name']} / {self.title}"
+ def post_contributors(self, collector=Collector("contributors")):
+ return collector.values()
```
-Applicable scenarios:
+> ⬆ This is **Level 4** in the demo: [`SprintDetail`](https://github.com/allmonday/sqlmodel-nexus/blob/master/demo/core_api/dtos.py#L107).
-- Child nodes need ancestor context (sprint name, permission info, tenant configuration)
-- Parent nodes need to aggregate results from multiple descendants (contributors, tags)
+## Custom Non-ORM Relationships
-## Resolver Options
+For data sources that don't use ORM relationships (external APIs, caches, computed edges), declare custom `Relationship` on SQLModel entities with a hand-written async loader:
```python
-result = await Resolver(
- context={"user_id": 42}, # Pass global context
- loader_params={}, # DataLoader extra parameters
-).resolve(dtos)
+from sqlmodel_nexus import Relationship as CustomRelationship
+
+async def tags_loader(task_ids: list[int]) -> list[list[Tag]]:
+ async with get_session() as session:
+ stmt = select(Tag, TaskTag.task_id).join(TaskTag).where(TaskTag.task_id.in_(task_ids))
+ rows = (await session.exec(stmt)).all()
+ return build_list(rows, task_ids, lambda r: r[1], lambda r: r[0])
+
+class Task(SQLModel, table=True):
+ __relationships__ = [
+ CustomRelationship(fk="id", target=list[Tag], name="tags", loader=tags_loader)
+ ]
```
-## Loader Dependency Name Rule
+DTO auto-loads custom relationships the same way as ORM ones:
+
+```python
+class TaskDTO(DefineSubset):
+ __subset__ = (Task, ("id", "title"))
+ tags: list[TagDTO] = [] # Auto-loaded from the custom relationship
+```
-`Loader('author')` requires a relationship named `author` in ErManager. When using implicit auto-loading, you typically don't need to write Loaders manually.
+> ⬆ This is **Level 5** in the demo: [`TaskWithTags`](https://github.com/allmonday/sqlmodel-nexus/blob/master/demo/core_api/dtos.py#L140).
-## Next Steps
+## Execution Order (Full)
-- [Custom Relationships](./custom_relationship.md) — Non-ORM relationship declarations
-- [MCP Service](../advanced/mcp_service.md) — Expose APIs to AI agents
+1. Execute all `resolve_*` methods on current node (load relationship data)
+2. Traverse existing object/relationship fields recursively (depth-first)
+3. Execute all `post_*` methods on current node (compute derived fields)
+4. Collect SendTo values upward to ancestor Collectors
diff --git a/docs/guide/graphql_mode.md b/docs/guide/graphql_mode.md
index d269066..3c89705 100644
--- a/docs/guide/graphql_mode.md
+++ b/docs/guide/graphql_mode.md
@@ -2,6 +2,10 @@
From SQLModel entities to a complete GraphQL API — SDL auto-generation, automatic relationship resolution, and DataLoader batch loading.
+> **Prerequisites**: You should be familiar with [SQLModel](https://sqlmodel.tiangolo.com/) entity definitions (SQLModel + Field + Relationship).
+>
+> **Live demo**: See [`demo/app.py`](https://github.com/allmonday/sqlmodel-nexus/blob/master/demo/app.py) for a working GraphQL server (User/Post/Comment).
+
## GraphQLHandler Configuration
```python
diff --git a/docs/guide/quick_start.md b/docs/guide/quick_start.md
index 0c29cb8..e43fee7 100644
--- a/docs/guide/quick_start.md
+++ b/docs/guide/quick_start.md
@@ -1,103 +1,138 @@
# Quick Start
-From SQLModel entities to a working GraphQL API in 30 seconds.
+Pick your path — three ways to use sqlmodel-nexus from the same SQLModel entities.
-## Installation
+All paths assume you have:
```bash
pip install sqlmodel-nexus
```
-## 1. Define SQLModel Entities
+---
+
+## 🟣 Path 1: GraphQL API
+
+Define entities, add `@query` / `@mutation`, create a `GraphQLHandler` — done.
```python
from sqlmodel import SQLModel, Field, Relationship, select
+from sqlmodel_nexus import query, mutation, GraphQLHandler
class User(SQLModel, table=True):
id: int | None = Field(default=None, primary_key=True)
name: str
- email: str
posts: list["Post"] = Relationship(back_populates="author")
+ @query
+ async def get_all(cls, limit: int = 10) -> list["User"]:
+ async with get_session() as session:
+ return (await session.exec(select(cls).limit(limit))).all()
+
class Post(SQLModel, table=True):
id: int | None = Field(default=None, primary_key=True)
title: str
author_id: int = Field(foreign_key="user.id")
author: User | None = Relationship(back_populates="posts")
-```
-
-## 2. Add @query and @mutation
-
-```python
-from sqlmodel_nexus import query, mutation
-
-class Post(SQLModel, table=True):
- # ... fields as above ...
-
- @query
- async def get_all(cls, limit: int = 10) -> list['Post']:
- """Get all posts."""
- async with get_session() as session:
- return (await session.exec(select(cls).limit(limit))).all()
@mutation
- async def create(cls, title: str, author_id: int) -> 'Post':
- """Create a new post."""
+ async def create(cls, title: str, author_id: int) -> "Post":
async with get_session() as session:
post = cls(title=title, author_id=author_id)
session.add(post)
await session.commit()
await session.refresh(post)
return post
+
+handler = GraphQLHandler(base=SQLModel, session_factory=async_session)
```
-## 3. Create GraphQLHandler
+```graphql
+# Query
+{ userGetAll(limit: 5) { id name posts { title } } }
-```python
-from fastapi import FastAPI
-from fastapi.responses import HTMLResponse
-from pydantic import BaseModel
-from sqlmodel_nexus import GraphQLHandler
+# Mutation
+mutation { postCreate(title: "Hello", authorId: 1) { id title } }
+```
-handler = GraphQLHandler(base=SQLModel, session_factory=async_session)
+📖 Learn more: [GraphQL Mode](./guide/graphql_mode.md)
-class GraphQLRequest(BaseModel):
- query: str
+---
-app = FastAPI()
+## 🟢 Path 2: REST API (Core API)
-@app.get("/graphql", response_class=HTMLResponse)
-async def graphiql():
- return handler.get_graphiql_html()
+Define `DefineSubset` DTOs — relationship fields auto-load via DataLoader.
-@app.post("/graphql")
-async def graphql(req: GraphQLRequest):
- return await handler.execute(req.query)
+```python
+from sqlmodel_nexus import DefineSubset, ErManager
+
+# 1. Define DTOs
+class UserDTO(DefineSubset):
+ __subset__ = (User, ("id", "name"))
+
+class PostDTO(DefineSubset):
+ __subset__ = (Post, ("id", "title", "author_id"))
+ author: UserDTO | None = None # auto-loaded! No resolve_* needed
+
+# 2. Create Resolver
+er = ErManager(base=SQLModel, session_factory=async_session)
+Resolver = er.create_resolver()
+
+# 3. In your FastAPI endpoint
+@app.get("/posts")
+async def get_posts():
+ posts = (await session.exec(select(Post).limit(10))).all()
+ dtos = [PostDTO(**p.model_dump()) for p in posts] # or use build_dto_select
+ return await Resolver().resolve(dtos) # resolves author automatically
```
-## 4. Run and Query
+Output JSON:
-```bash
-uvicorn app:app
-# Visit http://localhost:8000/graphql
+```json
+[
+ { "id": 1, "title": "Hello", "author": { "id": 1, "name": "Alice" } },
+ { "id": 2, "title": "World", "author": { "id": 2, "name": "Bob" } }
+]
```
-```graphql
-{
- postGetAll(limit: 5) {
- id
- title
- author { name email }
- }
-}
-```
+📖 Learn more: [Core API Mode](./guide/core_api.md)
-**Automatic relationship resolution**: The framework traverses the GraphQL selection tree, collects FK values layer by layer, and batch-loads relationships via DataLoader. No matter how many records are returned, each relationship requires only one query.
+---
-## Core Mental Model
+## 🟡 Path 3: MCP for AI Agents (UseCase)
+Write business logic once — serve both MCP and FastAPI from the same `UseCaseService` class.
+
+```python
+from sqlmodel_nexus import UseCaseService, UseCaseAppConfig, create_use_case_mcp_server
+
+class PostService(UseCaseService):
+ @query
+ async def list_posts(cls, limit: int = 10) -> list[PostDTO]:
+ stmt = build_dto_select(PostDTO)
+ async with async_session() as session:
+ rows = (await session.exec(stmt)).all()
+ dtos = [PostDTO(**dict(row._mapping)) for row in rows]
+ return await Resolver().resolve(dtos)
+
+mcp = create_use_case_mcp_server(apps=[
+ UseCaseAppConfig(name="blog", services=[PostService]),
+])
+mcp.run() # AI agents can: list_apps → list_services → describe_service → call
```
-SQLModel entities + @query decorators → GraphQL API (SDL + DataLoader auto-generated)
-```
-Next, learn about the full capabilities of [GraphQL Mode](./graphql_mode.md).
+📖 Learn more: [UseCase Service](./advanced/use_case_service.md) | [FastAPI Integration](./advanced/use_case_fastapi.md)
+
+---
+
+## What's Next
+
+| Topic | Guide |
+|-------|-------|
+| How DTOs auto-load relationships | [Core API Mode](./guide/core_api.md) |
+| Derived fields via `post_*` | [Core API Advanced](./guide/core_api_advanced.md) |
+| Pagination in GraphQL | [GraphQL Pagination](./guide/graphql_pagination.md) |
+| Non-ORM relationships | [Custom Relationships](./guide/custom_relationship.md) |
+| Five-level progressive demo | [`demo/core_api/dtos.py`](../demo/core_api/dtos.py) |
+| Full project template | [`skill/template/`](../skill/template/) |
+
+The remaining guides and API references are listed in the [index](index.md).
diff --git a/docs/index.md b/docs/index.md
index bce9d81..7134d8c 100644
--- a/docs/index.md
+++ b/docs/index.md
@@ -4,72 +4,132 @@ template: home.html
# sqlmodel-nexus
-**sqlmodel-nexus** is a progressive SQLModel extension library. Start from ORM entities, extend with non-ORM relationships, auto-generate GraphQL APIs, and use `DefineSubset` to declaratively build response DTOs. Visualize entity relationships and data flows through ER diagrams.
+**Define your SQLModel entities once. Get GraphQL, REST, and MCP APIs — with N+1 protection built in.**
-## What sqlmodel-nexus Solves
+```mermaid
+flowchart LR
+ M[SQLModel Entities] --> G[GraphQL API]
+ M --> R[REST API]
+ M --> MCP[MCP for AI Agents]
+ style M fill:#4a90d9,color:#fff
+ style G fill:#e535ab,color:#fff
+ style R fill:#22c55e,color:#fff
+ style MCP fill:#f59e0b,color:#fff
+```
-| Need | What You Write | What the Framework Handles |
-|------|----------------|---------------------------|
-| GraphQL API | `@query` / `@mutation` decorators | Auto-generate SDL, DataLoader batch-loading relationships |
-| REST / Use-case DTOs | `DefineSubset` + field declarations | Implicit auto-loading, N+1 prevention, ORM→DTO conversion |
-| Derived fields | `post_*` methods | Auto-execute after nested data is ready |
-| Cross-layer data flow | `ExposeAs`, `SendTo`, `Collector` | Pass context downward or aggregate results upward |
-| Non-ORM relationships | `Relationship(...)` | Same DataLoader infrastructure, supports auto-loading |
-| AI-ready API | `config_simple_mcp_server(base=...)` | Progressive MCP tool exposure |
+---
-## Use Cases
+## Pick Your Path
-- **Backend developers**: Quickly build GraphQL and REST APIs from SQLModel entities
-- **Teams**: Auto-generate APIs after models stabilize, reducing hand-written schemas
-- **Projects**: Support both GraphQL for validation and REST for delivery
-- **AI integration**: Expose the same models to AI agents via MCP
+| | GraphQL | REST (Core API) | MCP / UseCase |
+|---|---------|-----------------|---------------|
+| **What you get** | Auto-generated SDL + DataLoader-executed GraphQL endpoint | Pure Pydantic DTOs assembled via Resolver tree traversal | Four-layer progressive MCP tools for AI agents |
+| **Key API** | `@query` / `@mutation` + `GraphQLHandler` | `DefineSubset` + `ErManager` + `Resolver` | `UseCaseService` + `create_use_case_mcp_server` |
+| **Best for** | Frontend apps needing flexible queries | REST APIs, service-layer DTOs | AI agent integration (Claude, Cursor, etc.) |
+| **Demo** | [`demo/app.py`](https://github.com/allmonday/sqlmodel-nexus/blob/master/demo/app.py) | [`demo/core_api/`](https://github.com/allmonday/sqlmodel-nexus/blob/master/demo/core_api/) | [`demo/use_case/`](https://github.com/allmonday/sqlmodel-nexus/blob/master/demo/use_case/) |
+| **Quick Start** | ↓ [GraphQL Quick Start](#graphql-path) | ↓ [Core API Quick Start](#core-api-path) | ↓ [UseCase Quick Start](#usecase-path) |
-## Learning Path
+---
-```mermaid
-flowchart LR
- p1["P1: ER Diagram
SQLModel entities + non-ORM relationships
+ visualized ER diagram"]
- --> p2["P2: GraphQL API
@query / @mutation
SDL auto-generation + DataLoader"]
- --> p3["P3: Core API
DefineSubset DTOs
Implicit auto-loading + post_*"]
- --> p4["MCP / UseCase
AI agents + business services"]
+
+
+## 🟣 Path 1: GraphQL API
+
+Mark entity methods with `@query` / `@mutation`, get a full GraphQL schema + DataLoader:
+
+```python
+from sqlmodel import SQLModel, Field, Relationship, select
+from sqlmodel_nexus import query, mutation, GraphQLHandler
+
+class User(SQLModel, table=True):
+ id: int | None = Field(default=None, primary_key=True)
+ name: str
+ posts: list["Post"] = Relationship(back_populates="author")
+
+ @query
+ async def get_users(cls, limit: int = 10) -> list["User"]:
+ async with get_session() as session:
+ return (await session.exec(select(cls).limit(limit))).all()
+
+handler = GraphQLHandler(base=SQLModel, session_factory=async_session)
+# handler.execute("{ userGetAll(limit: 5) { id name posts { title } } }")
```
-The guide reuses the same business scenario:
+📖 Full guide: [GraphQL Mode](./guide/graphql_mode.md) · [Pagination](./guide/graphql_pagination.md) · [Auto Query](./guide/graphql_auto_query.md)
-```mermaid
-erDiagram
- Sprint ||--o{ Task : "has many"
- Task }o--|| User : "owner"
+---
+
+
+
+## 🟢 Path 2: REST API (Core API)
+
+Define `DefineSubset` DTOs — relationship fields auto-load via DataLoader. No SQL injection, no N+1:
+
+```python
+from sqlmodel_nexus import DefineSubset, ErManager
+
+class UserDTO(DefineSubset):
+ __subset__ = (User, ("id", "name"))
+
+class TaskDTO(DefineSubset):
+ __subset__ = (Task, ("id", "title", "owner_id"))
+ owner: UserDTO | None = None # Auto-loaded!
+
+er = ErManager(base=SQLModel, session_factory=async_session)
+Resolver = er.create_resolver()
+result = await Resolver().resolve(dtos) # Tree traversal resolves all relationships
+```
+
+📖 Full guide: [Core API Mode](./guide/core_api.md) · [Advanced](./guide/core_api_advanced.md) · [Custom Relationships](./guide/custom_relationship.md)
+
+---
+
+
+
+## 🟡 Path 3: MCP for AI Agents (UseCase)
+
+Write business logic once in `UseCaseService` — serve both MCP and FastAPI from the same class:
+
+```python
+from sqlmodel_nexus import UseCaseService, UseCaseAppConfig, create_use_case_mcp_server
+
+class SprintService(UseCaseService):
+ @query
+ async def list_sprints(cls) -> list[SprintSummary]:
+ dtos = [SprintSummary(**dict(row._mapping)) for row in rows]
+ return await Resolver().resolve(dtos)
+
+mcp = create_use_case_mcp_server(apps=[
+ UseCaseAppConfig(name="project", services=[SprintService], ...),
+])
+mcp.run() # Exposes 4-layer MCP tools: list_apps → list_services → describe_service → call
```
-### Guide (Tutorial Path)
-
-| Page | Main Question Answered |
-|------|------------------------|
-| [Quick Start](./guide/quick_start.md) | How to get a GraphQL API running with minimal code? |
-| [ER Diagram & Non-ORM Relationships](./guide/er_diagram.md) | How to declare and visualize entity relationships? |
-| [GraphQL Mode](./guide/graphql_mode.md) | What is the full workflow from SQLModel to GraphQL API? |
-| [GraphQL Pagination](./guide/graphql_pagination.md) | How to paginate list relationships? |
-| [Auto Query](./guide/graphql_auto_query.md) | How to skip @query and auto-generate by_id / by_filter? |
-| [Core API Mode](./guide/core_api.md) | How do DefineSubset + implicit auto-loading work? |
-| [Core API Advanced](./guide/core_api_advanced.md) | How to use resolve_* / post_* / cross-layer data flow? |
-| [Custom Relationships](./guide/custom_relationship.md) | How to declare and use non-ORM relationships? |
-| [ER Diagram Visualization](./guide/er_diagram_visual.md) | How to generate and embed Mermaid ER diagrams? |
-
-### Advanced Guides
-
-| Page | Topic |
-|------|-------|
-| [MCP Service](./advanced/mcp_service.md) | Expose SQLModel APIs to AI agents |
-| [UseCase Service](./advanced/use_case_service.md) | Define business services serving both MCP and REST |
-| [UseCase + FastAPI](./advanced/use_case_fastapi.md) | Embed the same service class into FastAPI routes |
-| [Voyager Visualization](./advanced/voyager.md) | Interactive ERD browsing |
-
-### API Reference
-
-- [GraphQLHandler](./api/api_graphql_handler.md) — GraphQL entry point + SDL generation
-- [Core API](./api/api_core.md) — ErManager / Resolver / DefineSubset / Loader
-- [Cross-layer Data Flow](./api/api_cross_layer.md) — ExposeAs / SendTo / Collector
-- [Relationships & ER Diagram](./api/api_relationship.md) — Relationship / ErDiagram
-- [MCP API](./api/api_mcp.md) — MCP service configuration
-- [UseCase API](./api/api_use_case.md) — UseCaseService / create_use_case_mcp_server
+📖 Full guide: [UseCase Service](./advanced/use_case_service.md) · [FastAPI](./advanced/use_case_fastapi.md) · [Voyager](./advanced/voyager.md) · [MCP Service](./advanced/mcp_service.md)
+
+---
+
+## Complete Tutorial
+
+For a step-by-step walkthrough with the same Sprint/Task model used across all demos, see [`demo/core_api/`](https://github.com/allmonday/sqlmodel-nexus/blob/master/demo/core_api/). The DTOs progress through 5 levels of complexity:
+
+| Level | What | File |
+|-------|------|------|
+| 1 | Basic field selection + FK hiding | [`dtos.py` → `UserSummary`](https://github.com/allmonday/sqlmodel-nexus/blob/master/demo/core_api/dtos.py#L21) |
+| 2 | Implicit relationship auto-loading | [`dtos.py` → `TaskSummary`](https://github.com/allmonday/sqlmodel-nexus/blob/master/demo/core_api/dtos.py#L30) |
+| 3 | `post_*` derived field computation | [`dtos.py` → `SprintSummary`](https://github.com/allmonday/sqlmodel-nexus/blob/master/demo/core_api/dtos.py#L53) |
+| 4 | Cross-layer data flow (`ExposeAs` / `SendTo` / `Collector`) | [`dtos.py` → `SprintDetail`](https://github.com/allmonday/sqlmodel-nexus/blob/master/demo/core_api/dtos.py#L107) |
+| 5 | Custom non-ORM relationships | [`dtos.py` → `TaskWithTags`](https://github.com/allmonday/sqlmodel-nexus/blob/master/demo/core_api/dtos.py#L140) |
+
+---
+
+## API Reference
+
+| Module | Key exports |
+|--------|-------------|
+| [GraphQLHandler](./api/api_graphql_handler.md) | `GraphQLHandler`, `SDLGenerator`, `AutoQueryConfig` |
+| [Core API](./api/api_core.md) | `ErManager`, `Resolver`, `DefineSubset`, `SubsetConfig`, `Loader` |
+| [Cross-layer Data Flow](./api/api_cross_layer.md) | `ExposeAs`, `SendTo`, `Collector` |
+| [Relationships & ER Diagram](./api/api_relationship.md) | `Relationship`, `ErDiagram` |
+| [MCP API](./api/api_mcp.md) | `config_simple_mcp_server`, `create_mcp_server` |
+| [UseCase API](./api/api_use_case.md) | `UseCaseService`, `UseCaseAppConfig`, `create_use_case_mcp_server`, `create_use_case_router` |
diff --git a/src/sqlmodel_nexus/__init__.py b/src/sqlmodel_nexus/__init__.py
index 5897082..08ca391 100644
--- a/src/sqlmodel_nexus/__init__.py
+++ b/src/sqlmodel_nexus/__init__.py
@@ -1,12 +1,21 @@
-"""SQLModel Nexus - GraphQL SDL generation and Core API response building.
-
-This package provides:
-- Automatic GraphQL SDL generation from SQLModel classes
-- @query/@mutation decorators for defining GraphQL operations
-- DataLoader-based relationship resolution
-- Per-relationship pagination support
-- DefineSubset for creating independent DTO models from SQLModel entities
-- ErManager for entity-relationship management and Resolver creation
+"""sqlmodel-nexus — from SQLModel entities to GraphQL / REST / MCP APIs.
+
+Define your data model once as SQLModel entities; get three API outputs
+automatically with built-in N+1 prevention via DataLoader batch loading.
+
+┌─ GraphQL mode ─────────── @query/@mutation → SDL auto-generation → GraphQLHandler
+├─ Core API (REST) ──────── DefineSubset DTO → ErManager → Resolver → FastAPI
+└─ UseCase (MCP) ────────── UseCaseService → create_use_case_mcp_server → AI agents
+
+Core capabilities:
+- @query/@mutation decorators: mark entity methods as GraphQL operations
+- SDLGenerator + GraphQLHandler: auto-generate SDL and execute queries
+- DataLoader batch loading: per-relationship, N+1-proof, optional pagination
+- DefineSubset: create pure Pydantic DTOs from SQLModel entities
+- ErManager + Resolver: model-driven tree traversal with implicit auto-loading
+- UseCaseService: one business logic class, two outputs (MCP + FastAPI)
+- Relationship: declare non-ORM relationships with custom DataLoaders
+- ErDiagram + Voyager: visualize entity graphs and service topologies
Example (GraphQL mode):
```python