README.rst
September 7, 2025 · View on GitHub
typed-graphql #############
What
- What if writing types for your GraphQL resolvers resulted in you defining your schema?
- Use Python types to create GraphQL schemas in less code.
- typed-graphl is a thin layer over graphql-core.
Why
- Much of your schema can be defined by using types, reducing code size.
- It's more flexible to use a thin layer over graphql-core, as opposed to a large framework.
- Minimal footprint: typed-graphql is significantly smaller than other GraphQL libraries.
Graphene:
.. code-block:: python :class: ignore
import graphene
class Query(graphene.ObjectType): hello = graphene.String(description='A typical hello world')
def resolve_hello(self, info):
return 'World'
schema = graphene.Schema(query=Query)
Typed-graphql:
.. code-block:: python :class: ignore
from graphql import graphql_sync from graphql.type import GraphQLSchema from typed_graphql import graphql_type, staticresolver
class Query: @staticresolver def hello(data, info) -> str: return 'World'
schema = GraphQLSchema(query=graphql_type(Query))
Library Size Comparison
typed-graphql has a much smaller installed size compared to other popular GraphQL libraries:
.. code-block::
Library Size Comparison (installed packages):
typed-graphql ██ 48.4 KB (1.0x) graphene ████████████████████████████████████████ 992.4 KB (20.5x larger) strawberry-graphql ████████████████████████████████████████████████████████████████████████████████████████████ 2.2 MB (47.1x larger)
This makes typed-graphql ideal for:
- Serverless environments where package size affects cold start times
- Lightweight applications that want minimal dependencies
- Docker images where every KB matters for deployment speed
Features
Python datetime to "Datetime" scalar
.. code-block:: python :class: ignore
from graphql import graphql_sync
@dataclass
class User:
login: datetime
created: datetime
class Query:
def resolve_user(self, info) -> List[User]:
return [
User(
datetime(2020, 1, 1, 0, 0, 0, tzinfo=timezone.utc),
datetime(2019, 1, 1, 1, 1, 1, tzinfo=timezone.utc),
)
]
Python date to "Date" scalar
.. code-block:: python :class: ignore
@dataclass
class User:
login: date
created: date
class Query:
def resolve_user(self, info) -> List[User]:
return [
User(
date(2020, 1, 1),
date(2019, 1, 1),
)
]
Native Python Enums
.. code-block:: python :class: ignore
class Status(enum.Enum): OFFLINE = "offline" ONLINE = "online" DORMANT = "dormant"
@dataclass
class User:
status: Status
class Query:
def resolve_user(self, info) -> List[User]:
return [User(Status.OFFLINE), User(Status.ONLINE)]
Argument defaults
.. code-block:: python :class: ignore
@dataclass
class User:
str: Name
class Query:
@staticresolver
def user(data, info, phone_number: Optional[str] = "000") -> List[User]:
return User("test")
Docstrings
.. code-block:: python :class: ignore
@dataclass
class User:
"""A user agent"""
value: str
class Query:
def resolve_user(self, info, pk: str) -> List[User]:
"""
:param pk: The primary key
"""
return [User("1")]
Installation
.. code-block:: bash :class: ignore
pip install typed-graphql
Philosophy
- Not a framework. If you want to do something off-script, go for it
- Python type driven GraphQL schemas
- Use Python builtins as much as possible (ie. dataclass, dict, TypedDict)
- Be a thin layer over graphql-core
Example
.. code-block:: python :class: ignore
from functools import partial from typing import Any, List, Optional, TypedDict
from graphql import graphql_sync from graphql.type import GraphQLSchema
from typed_graphql import graphql_type, staticresolver
class User(TypedDict):
# Regular method
@staticresolver
def name(data, info) -> str:
return data.get("name", "") + "1"
# Optional respects not null types
# Auto camelCases the attribute
@staticresolver
def optional_name(data, info) -> Optional[str]:
return data.get("name", "") + "1"
# Method with typed argument
@staticresolver
def addresses(data, info, limit: int) -> List[str]:
return ["address1", "address2"]
class Query: @staticresolver def users(data, info) -> List[User]: return [User(**{"name": "xxx", "status": False, "rate": 0.1})]
query = graphql_type(Query) schema = GraphQLSchema(query=graphql_type(Query))
QUERY = """ { users { name optionalName addresses(limit: 1) } } """
result = graphql_sync(schema, QUERY)
assert result.data == { "users": [ { "name": "xxx1", "optionalName": "xxx1", "addresses": ["address1", "address2"], } ] } assert result.errors is None