Skip to main content

Strawberry SQLAlchemy Mapper

PyPI Version python Downloads Test Coverage CI/CD Status

Discord

The simplest way to implement autogenerated Strawberry types for columns and relationships in SQLAlchemy models.

Instead of manually listing every column and relationship in a SQLAlchemy model, strawberry-sqlalchemy-mapper lets you decorate a class declaration and it will automatically generate the necessary strawberry fields for all columns and relationships (subject to the limitations below) in the given model.

  • Native support for most of SQLAlchemy's most common types. (See all supported types here)
  • Extensible to arbitrary custom SQLAlchemy types.
  • Automatic batching of queries, avoiding N+1 queries when getting relationships
  • Support for SQLAlchemy >=1.4.x
  • Lightweight and fast.

Table of Contents

Getting Started

Installation

strawberry-sqlalchemy-mapper is available on PyPi

pip install strawberry-sqlalchemy-mapper

Basic Usage

First, define your sqlalchemy model:

# models.py
from sqlalchemy import Column, ForeignKey, Integer, String
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import relationship

Base = declarative_base()


class Employee(Base):
    __tablename__ = "employee"
    id = Column(UUID, primary_key=True)
    name = Column(String, nullable=False)
    password_hash = Column(String, nullable=False)
    department_id = Column(UUID, ForeignKey("department.id"))
    department = relationship("Department", back_populates="employees")


class Department(Base):
    __tablename__ = "department"
    id = Column(UUID, primary_key=True)
    name = Column(String, nullable=False)
    employees = relationship("Employee", back_populates="department")

Next, decorate a type with strawberry_sqlalchemy_mapper.type() to register it as a strawberry type for the given SQLAlchemy model. This will automatically add fields for the model's columns, relationships, association proxies, and hybrid properties. For example:

# In another file
# ...
import strawberry
from strawberry_sqlalchemy_mapper import StrawberrySQLAlchemyMapper

strawberry_sqlalchemy_mapper = StrawberrySQLAlchemyMapper()


@strawberry_sqlalchemy_mapper.type(models.Employee)
class Employee:
    __exclude__ = ["password_hash"]


@strawberry_sqlalchemy_mapper.type(models.Department)
class Department:
    pass


@strawberry.type
class Query:
    @strawberry.field
    def departments(self):
        # This db.session was created with sqlalchemy.orm.sessionmaker(...)
        return db.session.scalars(select(models.Department)).all()


# context is expected to have an instance of StrawberrySQLAlchemyLoader
class CustomGraphQLView(GraphQLView):
    def get_context(self):
        return {
            "sqlalchemy_loader": StrawberrySQLAlchemyLoader(bind=YOUR_SESSION),
        }


# call finalize() before using the schema:
# (note that models that are related to models that are in the schema
# are automatically mapped at this stage -- e.g., Department is mapped
# because employee.department is a relationshp to Department)
strawberry_sqlalchemy_mapper.finalize()
# only needed if you have polymorphic types
additional_types = list(strawberry_sqlalchemy_mapper.mapped_types.values())
schema = strawberry.Schema(
    query=Query,
    mutation=Mutation,
    extensions=extensions,
    types=additional_types,
)

# You can now query, e.g.:
"""
query {
    departments {
        id
        name
        employees {
            edge {
                node {
                    id
                    name
                    department {
                        # Just an example of nested relationships
                        id
                        name
                    }
                }
            }
        }
    }
}
"""

Type Inheritance

You can inherit fields from other mapped types using standard Python class inheritance.

  • Fields from the parent type (e.g., ApiA) are inherited by the child (e.g., ApiB).

  • The __exclude__ setting applies to inherited fields.

  • If both SQLAlchemy models define the same field name, the field from the model inside .type(...) takes precedence.

  • Declaring a field manually in the mapped type overrides everything else.

class ModelA(base):
    __tablename__ = "a"

    id = Column(String, primary_key=True)
    common_field = Column(String(50))


class ModelB(base):
    __tablename__ = "b"

    id = Column(String, primary_key=True)
    common_field = Column(Integer)  # Conflicting field
    extra_field = Column(String(50))


@mapper.type(ModelA)
class ApiA:
    __exclude__ = ["id"]  # This field will be excluded in ApiA (and its children)


@mapper.type(ModelB)
class ApiB(ApiA):
    # Inherits fields from ApiA, except "id"
    # "common_field" will come from ModelB, not ModelA, so it will be a Integer
    # "extra_field" will be overridden and will be a float now instead of the String type declared in ModelB:
    extra_field: float = strawberry.field(name="extraField")

Relay connections

By default, StrawberrySQLAlchemyMapper() will create Relay connections for relationships to lists. If instead you want these relationships to present as plain lists, you have two options:

  1. Declare __use_list__ in your models, for example:
@strawberry_sqlalchemy_mapper.type(models.Department)
class Department:
    __use_list__ = ["employees"]
  1. Alternatively, you can disable relay style connections for all models via the always_use_list constructor parameter:
strawberry_sqlalchemy_mapper = StrawberrySQLAlchemyMapper(always_use_list=True)

BigInt Columns

SQLAlchemy BigInteger columns are mapped to a custom BigInt scalar. Its behaviour is defined in strawberry_sqlalchemy_scalar_map, which you must register on your schema:

import strawberry
from strawberry.schema.config import StrawberryConfig
from strawberry_sqlalchemy_mapper import strawberry_sqlalchemy_scalar_map

schema = strawberry.Schema(
    query=Query,
    config=StrawberryConfig(scalar_map=strawberry_sqlalchemy_scalar_map),
)

If you already have your own scalar_map, merge the two:

config = StrawberryConfig(
    scalar_map={**strawberry_sqlalchemy_scalar_map, **my_scalar_map},
)

Forgetting to register it raises TypeError: ... fields cannot be resolved. Unexpected type '...BigInt' when the schema is built.

Limitations

Supported Types

SQLAlchemy Models -> Strawberry Types and Interfaces are expected to have a consistent (customizable) naming convention. These can be configured by passing model_to_type_name and model_to_interface_name when constructing the mapper.

Natively supports the following SQLAlchemy types:

SQLAlchemy Type Strawberry Equivalent Notes
Integer int
Float float
BigInteger BigInt Requires registering strawberry_sqlalchemy_scalar_map on your schema (see BigInt Columns)
Numeric Decimal
DateTime datetime
Date date
Time time
String str
Text str
Boolean bool
Unicode str
UnicodeText str
SmallInteger int
SQLAlchemyUUID uuid.UUID
VARCHAR str
ARRAY[T] List[T] PostgreSQL array
JSON JSON SQLAlchemy JSON
Enum enum.Enum The Python enum that the column is mapped to must be decorated with@strawberry.enum (strawberry enum docs)

Additional types can be supported by passing extra_sqlalchemy_type_to_strawberry_type_map, although support for TypeDecorator types is untested.

Association Proxies

Association proxies are expected to be of the form association_proxy('relationship1', 'relationship2'), i.e., both properties are expected to be relationships. If your association_proxy does not follow the expected form, you should add it to __exclude__ to prevent an exception from being raised.

Polymorphic Hierarchies

Roots of polymorphic hierarchies are supported, but are also expected to be registered via strawberry_sqlalchemy_mapper.interface(), and its concrete type and its descendants are expected to inherit from the interface:

# models.py
from sqlalchemy import Column

Base = declarative_base()


class Book(Base):
    id = Column(UUID, primary_key=True)


class Novel(Book):
    pass


class ShortStory(Book):
    pass


# in another file
strawberry_sqlalchemy_mapper = StrawberrySQLAlchemyMapper()


@strawberry_sqlalchemy_mapper.interface(models.Book)
class BookInterface:
    pass


@strawberry_sqlalchemy_mapper.type(models.Book)
class Book:
    pass


@strawberry_sqlalchemy_mapper.type(models.Novel)
class Novel:
    pass


@strawberry_sqlalchemy_mapper.type(models.ShortStory)
class ShortStory:
    pass

Contributing

We encourage you to contribute to strawberry-sqlalchemy-mapper! Any contributions you make are greatly appreciated.

If you have a suggestion that would make this better, please fork the repo and create a pull request. Don't forget to give the project a star! Thanks again!

  1. Fork the Project
  2. Create your Feature Branch (git checkout -b feature)
  3. Commit your Changes (git commit -m 'Add some feature')
  4. Push to the Branch (git push origin feature)
  5. Open a Pull Request

For more details on how to contribute, as well as how to setup the project on your local machine, please refer to the contributing docs

Prerequisites

This project uses pre-commit, please make sure to install it before making any changes::

pip install pre-commit
cd strawberry-sqlalchemy-mapper
pre-commit install

Don't forget to tell your contributors to also install and use pre-commit.

💡 Tip: You can also use our DevContainer setup for a fully configured development environment, including pre-commit, Python, PostgreSQL, and all required dependencies. This is the fastest way to get started.

Installation

pip install -r requirements.txt

Install PostgreSQL 14+

Test

pytest

⚖️ LICENSE

MIT © strawberry-sqlalchemy-mapper

Metadata

Release files for strawberry-sqlalchemy-mapper 0.9.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for strawberry-sqlalchemy-mapper 0.9.0
File Size Uploaded
strawberry_sqlalchemy_mapper-0.9.0.tar.gz 30.6 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for strawberry-sqlalchemy-mapper 0.9.0
File Interpreter ABI Platform
strawberry_sqlalchemy_mapper-0.9.0-py3-none-any.whl Python 3 none any Details

Total release size: 60.1 kB

Release files / strawberry_sqlalchemy_mapper-0.9.0.tar.gz

Download URL strawberry_sqlalchemy_mapper-0.9.0.tar.gz
Size 30.6 kB
Tags Source
SHA-256 checksum
How to use checksums
334838d274ae987a2afa4104899d3f5b2cb082e4a9ae4c64043a9f9822549e1c
BLAKE2b-256 checksum
How to use checksums
9e2598c81c84197cdc1b1c83367cd14fc290ae91853c6c910ec35719454413ec
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via poetry/2.4.2 CPython/3.10.21 Linux/6.17.0-1022-azure

Release files / strawberry_sqlalchemy_mapper-0.9.0-py3-none-any.whl

Download URL strawberry_sqlalchemy_mapper-0.9.0-py3-none-any.whl
Size 29.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
7879e0fad3c4fa97ff6f273857b0bfb339e1bd9af8944ae08e5c3dc11dd2f970
BLAKE2b-256 checksum
How to use checksums
e4db58955820f932c128072e28a404730715c1a67467ca351a02070d5f8e1c27
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via poetry/2.4.2 CPython/3.10.21 Linux/6.17.0-1022-azure

Release history Release notifications | RSS feed

This release

0.9.0 This release

2 release files

0.8.0

2 release files

0.7.0

2 release files

0.6.4

2 release files

0.6.3

2 release files

0.6.2

2 release files

0.6.1

2 release files

0.6.0

2 release files

0.5.0

2 release files

0.4.5

2 release files

0.4.4

2 release files

0.4.3

2 release files

0.4.2

2 release files

0.4.1

2 release files

0.4.0

2 release files

0.3.1

2 release files

0.3.0

2 release files

0.2.1

2 release files

0.2.0

2 release files

0.1.4

2 release files

0.1.3

2 release files

0.1.2

2 release files

0.1.1

2 release files

0.1.0

1 release file

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page