Model your business entities, relationships, and use cases once — GraphQL, REST, MCP, CLI, and TS SDK all derive from it. Data is a graph; tools are just its projections.
pip install nexusx
The same typed business model serves AI agents and developers as first-class consumers.
MCP is a native protocol: strongly typed, GraphQL under the hood.
Write SQLModel entities and typed DTOs; that is the whole job.
LLMs generate code fast — but without structural constraints, the debt surfaces weeks later: duplicated logic, components bleeding into each other, debugging by guesswork. The industry calls it the cost of vibe coding.
nexusx narrows what AI writes to a declarative model — entities, relationships, and typed use-case methods. Structure is not something the AI has to get right; it is guaranteed by the model.
AI writes the model and use-case methods — not scattered glue code. Small diffs, reviewable by humans.
Change a business rule once; every protocol updates in sync. Maintenance cost does not multiply per delivery.
Typed contracts, plus Voyager: entities, relationships, use cases, and their dependencies rendered as one live ER view — grasp the whole project without reading code first, whether you are a new human or a fresh AI session. Voyager →
Semantic-level isomorphism — every protocol is generated from the same typed model, not wrapped around a copy of it.
@app.get("/sprints")
async def rest_list_sprints() -> list[SprintOut]:
... # query + assembly, again
@strawberry.field
async def graphql_sprints(self) -> list[SprintType]:
... # types + s, again
@mcp.tool()
async def sprints_for_agents() -> str:
... # JSON dumping, again
class SprintService(UseCaseService):
"""Sprint planning operations."""
@query
async def list_sprints(cls) -> list[SprintSummary]:
"""List sprints with tasks, owners, and task count."""
return await load_sprints()
Typed FastAPI route, visible in OpenAPI.
create_use_case_router(api)
Entities become by_id / by_filter roots for exploring connected data.
Sprint { by_filter(limit: 10) { ... } }
Use-case methods become typed fields via the compose schema.
compose_query(app, query, args)
Agents discover it progressively.
create_use_case_graphql_mcp_server([api])
Services become command groups.
list_sprints --select "name task_count"
Typed client generated from the compose schema.
sprintService.listSprints()
Two GraphQL surfaces for two different jobs — use either one, or both.
SQLModel entities and relationships become by_id / by_filter query roots. No relationship resolvers to write — Data batching keeps it N+1-proof as callers traverse.
GraphQLHandlerTyped business methods expose stable capabilities to web clients, integrations, and AI agents — served over REST, MCP, CLI, and SDK from one definition.
UseCaseServiceEntities are not API contracts. DefineSubset hides internal columns, auto-loads relationships, and computes derived fields.
async def get_sprints():
sprints = await session.exec(select(Sprint))
result = []
for s in sprints:
tasks = await session.exec(
select(Task).where(Task.sprint_id == s.id))
for t in tasks:
t.owner = await session.get(User, t.owner_id)
python
from nexusx import DefineSubset, ErManager, build_dto_select
class UserDTO(DefineSubset):
__subset__ = (User, ("id", "name"))
class TaskDTO(DefineSubset):
__subset__ = (Task, ("id", "title", "owner_id"))
owner: UserDTO | None = None # auto-loaded
class SprintDTO(DefineSubset):
__subset__ = (Sprint, ("id", "name"))
tasks: list[TaskDTO] = [] # auto-loaded
er = ErManager(entities=[User, Sprint, Task], session_factory=async_session)
Resolver = er.create_resolver()
async def load_sprints() -> list[SprintDTO]:
stmt = build_dto_select(SprintDTO) # root columns only
async with async_session() as session:
rows = (await session.exec(stmt)).all()
dtos = [SprintDTO(**dict(r._mapping)) for r in rows]
return await Resolver().resolve(dtos) # tree filled, batched
The same relationship model stretches into more advanced architectures.
Data batching, SQL column pruning, window-function pagination — and total_count computed only when the response asks for it.
post_* for aggregations, ExposeAs / SendTo for cross-layer data flow.
Ordinary Pydantic models as non-table graph roots — Redis, search, and SDK-backed data join the same graph.
Compose multiple nexusx data graphs without a central gateway — homogeneous federation of nexusx services.
ComposedErManager composes multiple engines in one process; DTO federation loads public DTO trees across services.
Independently packaged applications and databases, combined into a single MCP server.
The principles that shape every API decision.
One field selection shapes the GraphQL response, the SQL columns loaded, the DTO fields copied, the MCP output, CLI --select, and whether total_count is even computed.
Redis, search engines, other databases, external APIs — declare a Relationship with an async batch function and they join the same , DTO, GraphQL, and ER-diagram infrastructure.
Business methods depend on no protocol object — builders inspect the typed signature and attach REST / MCP / CLI / SDK adapters. FromContext injects trusted values (user, tenant) without exposing them as client arguments.
Install the 4-phase skill into your coding agent — Claude Code, Codex, Cursor, and more — then describe your app in plain words. The agent drives the workflow; you review the model.
npx skills add KLR-Pattern/nexusx -s nexusx-4phase -a claude-code
Confirm the domain model and persistence strategy with you before any code is written.
Entities and relationships, GraphQL helper surface, then UseCase REST / MCP / CLI deliveries.
Optionally emit a typed TypeScript SDK from the compose schema.
Works with your existing frameworks and tools.
Declare the model once — the data graph, response DTOs, and every delivery follow.