Strawberry GraphQL
MD-Models provides GraphQL integration that automatically generates a GraphQL schema and server from your markdown-defined data models. Using Strawberry GraphQL, MD-Models creates a fully functional GraphQL API with automatic type generation, query resolvers, and nested relationship support.
The GraphQL integration transforms your markdown data models into GraphQL types and queries, allowing you to expose your data through a flexible GraphQL API. Each object type in your markdown becomes a GraphQL type with automatic query fields. This means you get a production-ready GraphQL server without writing any GraphQL schema definitions or resolver functions manually—MD-Models handles all the boilerplate for you.
GraphQL offers several advantages over traditional REST APIs: clients can request exactly the fields they need (avoiding over-fetching), related data can be fetched in a single query, and the schema is self-documenting through introspection. MD-Models leverages these benefits by automatically generating a GraphQL schema that matches your data model structure, ensuring type safety and consistency between your markdown definitions and your API.
Setting up the GraphQL server
Section titled “Setting up the GraphQL server”-
Load Your Data Model
First, you need to load your data model.
from mdmodels import DataModel, sqlfrom mdmodels.graphql import create_graphql_appmodel = DataModel.from_markdown("model.md")This gives you a Library containing Pydantic models (for request/response validation).
-
Set Up Database Connection
db = sql.DatabaseConnector(library=model, **database_config)# Creates tables and returns SQLModel classesdb_models = db.create_tables()The
DatabaseConnectorautomatically generates SQLModel classes from your library. Thecreate_tables()method returns the generated SQLModel classes (for database operations). -
Create GraphQL App
app = create_graphql_app(db=db) -
Run the Server
Terminal window uvicorn app:app --reload
CLI Usage
Section titled “CLI Usage”GraphQL is exposed through the REST server, so the CLI entry point is mdmodels rest with the --graphql flag:
mdmodels rest --config config.toml --graphql --host 127.0.0.1 --port 8800This starts FastAPI endpoints and mounts GraphQL at /graphql in the same app instance. For TOML configuration details (model, sql, rest, and mcp sections), see CLI Configuration.
Understanding Generated GraphQL Types
Section titled “Understanding Generated GraphQL Types”Each object type in your markdown becomes a GraphQL type. MD-Models uses Strawberry’s Pydantic integration to automatically convert your Pydantic models into GraphQL types. This conversion preserves all the type information from your markdown definitions, including field types, optional fields, and relationships.
The type conversion process is seamless: scalar types (strings, numbers, booleans) map directly to GraphQL scalars, while relationships become nested GraphQL types. This ensures that your GraphQL schema accurately represents your data model structure, and clients can query related data in a single request.
For example, if you define a model in markdown:
### Molecule- id: string- name: string- formula: stringThis becomes a GraphQL type:
type Molecule { id: String name: String formula: String}All fields from your markdown definition are included in the GraphQL type, and relationships are automatically handled through nested types. When you query a type that has relationships, GraphQL automatically resolves those relationships and includes the nested data in the response. This means you can traverse your entire data model structure in a single query, fetching molecules, their reactions, and related experiments all at once.
The type system ensures type safety: required fields are marked as non-nullable (using !), optional fields remain nullable, and list types are properly represented. This matches the structure defined in your markdown, so there’s no mismatch between your data definitions and your API schema.
Query Fields
Section titled “Query Fields”For each model in your markdown, MD-Models creates a query field that returns a list of that type. These query fields are automatically added to the root Query type, which is the entry point for all GraphQL queries. The naming convention uses a lowercased model name (e.g., Molecule becomes molecule).
Here’s what the generated query fields look like:
type Query { molecule(limit: Int = 100, offset: Int = 0): [Molecule!]! reaction(limit: Int = 100, offset: Int = 0): [Reaction!]! experiment(limit: Int = 100, offset: Int = 0): [Experiment!]!}Example without pagination or semantic search:
{ experiment { id name }}Each query field supports pagination through limit and offset parameters. The resolvers automatically query the database using SQLModel and return nested data structures including relationships. When you request related data in your query, the resolver fetches it from the database and includes it in the response, maintaining the hierarchical structure defined in your data model.
Semantic Search Fields
Section titled “Semantic Search Fields”When you have vector embeddings configured on your tables (see Vector Search), MD-Models automatically adds semantic search capabilities to your GraphQL query fields. This allows you to perform similarity searches using natural language queries or other embedded content like protein sequences. The semantic search functionality is exposed through two additional optional parameters on each query field:
-
semantic_queryaccepts optional text to embed and rank results by vector similarity, and if omitted, the query behaves as a plain list with pagination only. -
embedding_tableparameter accepts an optional table name whose embedding model and column should be used for the search, and if omitted, the current table’s embedder is used. This is particularly useful when you want to query one type (e.g.,experiment) using another table’s embedding space (e.g.,Proteinsequences).
In this example, we search for experiments by protein sequence space:
{ experiment( semantic_query: "MENFQKVEKIGEGTYGVVYKARNKLTGEVVALKKIRLEFDTDVLKVL..." embedding_table: "Protein" limit: 5 ) { id name }}Integration with FastAPI
Section titled “Integration with FastAPI”You can easily integrate your GraphQL API with a FastAPI application. This allows you to combine the flexibility of GraphQL with the simplicity of REST endpoints in a single application. You might use REST endpoints for simple CRUD operations and GraphQL for complex queries that need to traverse relationships or fetch specific field combinations.
When integrating with FastAPI, you can mount the GraphQL app as a router at any path you choose (commonly /graphql). The GraphQL endpoint will work alongside your other FastAPI routes, sharing the same application instance and middleware stack. This means you can apply FastAPI middleware (like CORS, authentication, or logging) to both REST and GraphQL endpoints.
Here’s how to integrate GraphQL with FastAPI:
from fastapi import FastAPIfrom mdmodels.graphql import create_graphql_app
fastapi_app = FastAPI(title="My API")
# Create and mount GraphQL appgraphql_app = create_graphql_app( db=db, as_router=True,)fastapi_app.include_router(graphql_app, prefix="/graphql")
# Add other FastAPI routes as needed@fastapi_app.get("/")def root(): return {"message": "API is running"}This allows you to combine GraphQL queries with REST endpoints in a single application, giving you the flexibility to use the best API style for each use case. REST endpoints are great for simple operations and when you want predictable URLs, while GraphQL excels at complex queries and when clients need to request specific data shapes.
Both interfaces share the same underlying data models and database connection, ensuring consistency across your API. You can use FastAPI’s dependency injection system to add authentication, authorization, or other middleware that applies to both REST and GraphQL endpoints. This unified approach gives you the benefits of both API styles without duplicating your data access logic.