GraphQL

flarchitect can expose SQLAlchemy models through a GraphQL API. The flarchitect.graphql.create_schema_from_models() helper builds a Graphene schema from your models, while flarchitect.Architect.init_graphql() registers a /graphql endpoint and documents it in the OpenAPI spec.

Quick start

The simplest way to enable GraphQL is to feed your models to create_schema_from_models and register the resulting schema with the architect:

schema = create_schema_from_models([User], db.session)
architect.init_graphql(schema=schema)

The generated schema provides CRUD-style queries and mutations for each model. An all_items query returns every Item and accepts optional column arguments for filtering. Pagination is supported via limit and offset arguments. create_item, update_item and delete_item mutations manage individual records.

Type mapping

create_schema_from_models converts SQLAlchemy column types into Graphene scalars using flarchitect.graphql.SQLA_TYPE_MAPPING. Out of the box it supports Integer, String, Boolean, Float, Date, DateTime, Numeric, JSON and UUID columns. Custom or proprietary SQLAlchemy types can be mapped by providing a type_mapping override:

schema = create_schema_from_models(
    [User], db.session, type_mapping={MyType: graphene.String}
)

Example mutations

create_schema_from_models automatically generates create_<table>, update_<table> and delete_<table> mutations. Each accepts the model’s columns as arguments with the primary key required for updates and deletions. The examples below create, update and delete an Item:

mutation {
    create_item(name: "Foo") {
        id
        name
    }
}

mutation {
    update_item(id: 1, name: "Bar") {
        id
        name
    }
}

mutation {
    delete_item(id: 1)
}

Example query

query {
    all_items(name: "Foo", limit: 1, offset: 0) {
        id
        name
    }
}

Filtering on any column is supported. The following returns all Item objects with name equal to "Bar":

query {
    all_items(name: "Bar") {
        id
        name
    }
}

Visit /graphql in a browser to access the interactive GraphiQL editor served on GET requests. Programmatic clients should send HTTP POST requests with a query payload.

Advanced usage

Custom type mappings

flarchitect maps common SQLAlchemy column types to Graphene scalars via the SQLA_TYPE_MAPPING dictionary. Extend this mapping to support application specific types:

from datetime import datetime
import graphene
from flarchitect.graphql import SQLA_TYPE_MAPPING

SQLA_TYPE_MAPPING[datetime] = graphene.DateTime

Relationships

create_schema_from_models automatically inspects SQLAlchemy relationships and adds fields returning the related object types. The example below links Item to Category so a query for items can also retrieve the owning category. Relationships are eagerly loaded using joinedload to avoid N+1 query issues.

class Category(db.Model):
    id = mapped_column(Integer, primary_key=True)
    name = mapped_column(String)

class Item(db.Model):
    id = mapped_column(Integer, primary_key=True)
    name = mapped_column(String)
    category_id = mapped_column(ForeignKey("category.id"))
    category = relationship(Category)

Item now exposes a category field and Category a items field. A single request can retrieve nested data:

query {
    all_items {
        name
        category { name }
    }
}

Filtering and pagination

Queries accept optional limit and offset arguments to page through large datasets. Additional arguments can be introduced to perform simple filtering:

query {
    all_items(name: "Foo", limit: 5, offset: 10) {
        id
        name
    }
}

CRUD mutations

create_schema_from_models exposes a full set of CRUD mutations out of the box, letting clients insert, modify and remove records without manual schema definitions.

Tips and trade-offs

GraphQL offers flexible queries and reduces the number of HTTP round-trips, but it also introduces additional complexity. Responses are not cacheable by standard HTTP mechanisms, and naïve schemas can allow very expensive queries. Ensure resolvers validate user input and consider depth limiting or query cost analysis for production deployments.

Further examples are available in demo.graphql.