- 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.
- 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:
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:
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))typed-graphql has a much smaller installed size compared to other popular GraphQL libraries:
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
Python datetime to "Datetime" scalar
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
@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
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
@dataclass class User: str: Name class Query: @staticresolver def user(data, info, phone_number: Optional[str] = "000") -> List[User]: return User("test")Docstrings
@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")]pip install typed-graphql- 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
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