CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-strawberry-graphql

A library for creating GraphQL APIs using dataclasses and type annotations with extensive framework integration support.

Overview
Eval results
Files

federation.mddocs/

Apollo Federation

Apollo Federation support for building distributed GraphQL architectures with multiple services and schema composition. Federation allows you to split your GraphQL schema across multiple services while presenting a unified API to clients.

Capabilities

Federation Schema

Federation-enabled schema class with support for Apollo Federation directives.

class Schema:
    """Apollo Federation schema implementation."""
    
    def __init__(
        self,
        query: Type = None,
        mutation: Type = None,
        subscription: Type = None,
        *,
        types: List[Type] = None,
        extensions: List[SchemaExtension] = None,
        enable_federation_2: bool = False
    ):
        """
        Initialize Apollo Federation schema.
        
        Args:
            query: Root query type
            mutation: Root mutation type (optional)
            subscription: Root subscription type (optional)
            types: Additional types to include in schema
            extensions: Schema extensions
            enable_federation_2: Enable Apollo Federation 2 features
        """

Usage Example:

import strawberry
from strawberry.federation import Schema

@strawberry.federation.type(keys=["id"])
class User:
    id: strawberry.ID
    name: str
    email: str

@strawberry.type
class Query:
    @strawberry.field
    def users(self) -> List[User]:
        return get_all_users()

# Create federation schema
schema = Schema(
    query=Query,
    enable_federation_2=True
)

Federation Type Decorator

Creates federated GraphQL object types with entity keys and federation directives.

def type(
    cls=None,
    *,
    name: str = None,
    description: str = None,
    keys: List[str] = None,
    extend: bool = False,
    resolvable: bool = True
) -> Any:
    """
    Decorator to create federated GraphQL object types.
    
    Args:
        cls: The class to convert to a federated type
        name: Custom name for the GraphQL type
        description: Description for the GraphQL type
        keys: List of key fields for entity resolution
        extend: Whether this extends an existing entity from another service
        resolvable: Whether this entity can be resolved by this service
    
    Returns:
        Federated GraphQL object type
    """

Usage Examples:

# Basic federated entity
@strawberry.federation.type(keys=["id"])
class User:
    id: strawberry.ID
    name: str
    email: str

# Multi-key entity
@strawberry.federation.type(keys=["id", "email"])
class User:
    id: strawberry.ID
    name: str
    email: str

# Extending entity from another service
@strawberry.federation.type(keys=["id"], extend=True)
class User:
    id: strawberry.ID = strawberry.federation.field(external=True)
    posts: List["Post"]
    
    @classmethod
    def resolve_reference(cls, id: strawberry.ID):
        # This service doesn't store user data, just references
        return cls(id=id)

# Non-resolvable entity (just provides additional fields)
@strawberry.federation.type(keys=["id"], resolvable=False)
class User:
    id: strawberry.ID = strawberry.federation.field(external=True)
    computed_field: str

Federation Field Decorator

Defines federated GraphQL fields with federation-specific directives.

def field(
    resolver: Callable = None,
    *,
    name: str = None,
    description: str = None,
    external: bool = False,
    requires: str = None,
    provides: str = None,
    override_: str = None,
    used_overridden: bool = False
) -> Any:
    """
    Decorator to define federated GraphQL fields.
    
    Args:
        resolver: Custom resolver function for the field
        name: Custom field name
        description: Field description
        external: Whether field is defined in another service
        requires: Fields required from other services to resolve this field
        provides: Fields this field provides to the entity
        override_: Service that this field overrides (Federation 2)
        used_overridden: Whether this field uses overridden field (Federation 2)
    
    Returns:
        Configured federated GraphQL field
    """

Usage Examples:

@strawberry.federation.type(keys=["id"])
class User:
    id: strawberry.ID
    name: str = strawberry.federation.field(external=True)
    email: str = strawberry.federation.field(external=True)
    
    @strawberry.federation.field(requires="name email")
    def display_name(self) -> str:
        return f"{self.name} <{self.email}>"
    
    @strawberry.federation.field(provides="title")
    def latest_post(self) -> "Post":
        post = get_latest_post(self.id)
        return Post(id=post.id, title=post.title)

@strawberry.federation.type(keys=["id"])
class Post:
    id: strawberry.ID
    title: str = strawberry.federation.field(external=True)
    content: str
    
    # Override field from another service (Federation 2)
    @strawberry.federation.field(override_="posts-service")
    def view_count(self) -> int:
        return get_accurate_view_count(self.id)

Federation Interface

Creates federated GraphQL interfaces.

def interface(
    cls=None,
    *,
    name: str = None,
    description: str = None,
    keys: List[str] = None
) -> Any:
    """
    Decorator to create federated GraphQL interfaces.
    
    Args:
        cls: The class to convert to a federated interface
        name: Custom name for the GraphQL interface
        description: Description for the GraphQL interface
        keys: Key fields for interface entities
    
    Returns:
        Federated GraphQL interface type
    """

Usage Example:

@strawberry.federation.interface(keys=["id"])
class Node:
    id: strawberry.ID

@strawberry.federation.type(keys=["id"])
class User(Node):
    id: strawberry.ID
    name: str

@strawberry.federation.type(keys=["id"])
class Post(Node):
    id: strawberry.ID
    title: str

Other Federation Decorators

All core decorators are available with federation support:

def input(
    cls=None,
    *,
    name: str = None,
    description: str = None
) -> Any:
    """Federated GraphQL input type decorator."""

def enum(
    cls=None,
    *,
    name: str = None,
    description: str = None
) -> Any:
    """Federated GraphQL enum type decorator."""

def scalar(
    cls=None,
    *,
    name: str = None,
    description: str = None,
    serialize: Callable = None,
    parse_value: Callable = None
) -> Any:
    """Federated GraphQL scalar type decorator."""

def union(name: str, types: Tuple[Type, ...]) -> Any:
    """Federated GraphQL union type creator."""

Entity Resolution

Entity Reference Resolution

Federated entities must implement reference resolution for the Apollo Gateway.

# Entities automatically get a resolve_reference class method
@strawberry.federation.type(keys=["id"])
class User:
    id: strawberry.ID
    name: str
    email: str
    
    @classmethod
    def resolve_reference(cls, id: strawberry.ID):
        """
        Resolve entity reference from Apollo Gateway.
        
        Args:
            id: Entity key value
        
        Returns:
            Entity instance
        """
        user_data = get_user_by_id(id)
        return cls(
            id=user_data["id"],
            name=user_data["name"],
            email=user_data["email"]
        )

Complex Key Resolution

For entities with compound keys:

@strawberry.federation.type(keys=["userId", "productId"])
class UserProductPreference:
    user_id: strawberry.ID
    product_id: strawberry.ID
    rating: int
    
    @classmethod
    def resolve_reference(cls, user_id: strawberry.ID, product_id: strawberry.ID):
        preference = get_user_product_preference(user_id, product_id)
        return cls(
            user_id=preference["user_id"],
            product_id=preference["product_id"],
            rating=preference["rating"]
        )

Multiple Key Variants

For entities that can be resolved by different key combinations:

@strawberry.federation.type(keys=["id", "email"])
class User:
    id: strawberry.ID
    email: str
    name: str
    
    @classmethod
    def resolve_reference(cls, **kwargs):
        if "id" in kwargs:
            user_data = get_user_by_id(kwargs["id"])
        elif "email" in kwargs:
            user_data = get_user_by_email(kwargs["email"])
        else:
            raise ValueError("No valid key provided")
        
        return cls(
            id=user_data["id"],
            email=user_data["email"],
            name=user_data["name"]
        )

Federation Patterns

Service Composition Example

Users Service:

# users_service.py
@strawberry.federation.type(keys=["id"])
class User:
    id: strawberry.ID
    name: str
    email: str
    created_at: datetime
    
    @classmethod
    def resolve_reference(cls, id: strawberry.ID):
        user = get_user_from_database(id)
        return cls(
            id=user.id,
            name=user.name,
            email=user.email,
            created_at=user.created_at
        )

@strawberry.type
class Query:
    @strawberry.field
    def user(self, id: strawberry.ID) -> Optional[User]:
        return User.resolve_reference(id)
    
    @strawberry.field
    def users(self) -> List[User]:
        return [User.resolve_reference(u.id) for u in get_all_users()]

schema = strawberry.federation.Schema(query=Query)

Posts Service:

# posts_service.py
@strawberry.federation.type(keys=["id"], extend=True)
class User:
    id: strawberry.ID = strawberry.federation.field(external=True)
    
    @strawberry.federation.field
    def posts(self) -> List["Post"]:
        return get_posts_by_user_id(self.id)

@strawberry.federation.type(keys=["id"])
class Post:
    id: strawberry.ID
    title: str
    content: str
    user_id: strawberry.ID
    published_at: datetime
    
    @strawberry.federation.field
    def author(self) -> User:
        return User(id=self.user_id)
    
    @classmethod
    def resolve_reference(cls, id: strawberry.ID):
        post = get_post_from_database(id)
        return cls(
            id=post.id,
            title=post.title,
            content=post.content,
            user_id=post.user_id,
            published_at=post.published_at
        )

@strawberry.type
class Query:
    @strawberry.field
    def post(self, id: strawberry.ID) -> Optional[Post]:
        return Post.resolve_reference(id)

schema = strawberry.federation.Schema(query=Query, types=[User])

Advanced Federation Features

Federation 2 Features

# Enable Federation 2 features
schema = strawberry.federation.Schema(
    query=Query,
    enable_federation_2=True
)

@strawberry.federation.type(keys=["id"])
class User:
    id: strawberry.ID
    name: str
    
    # Override field from another service
    @strawberry.federation.field(override_="legacy-service")
    def email(self) -> str:
        return get_updated_email(self.id)

# Shareable fields (can be defined in multiple services)
@strawberry.federation.type(keys=["id"])
class Product:
    id: strawberry.ID
    
    @strawberry.federation.field(shareable=True)
    def name(self) -> str:
        return self._name

Computed Fields with Requirements

@strawberry.federation.type(keys=["id"], extend=True)
class User:
    id: strawberry.ID = strawberry.federation.field(external=True)
    first_name: str = strawberry.federation.field(external=True)
    last_name: str = strawberry.federation.field(external=True)
    
    @strawberry.federation.field(requires="firstName lastName")
    def full_name(self) -> str:
        return f"{self.first_name} {self.last_name}"

Providing Fields to Other Services

@strawberry.federation.type(keys=["id"])
class User:
    id: strawberry.ID
    email: str
    
    @strawberry.federation.field(provides="username")
    def profile(self) -> "UserProfile":
        profile = get_user_profile(self.id)
        return UserProfile(
            user_id=self.id,
            username=derive_username_from_email(self.email),
            bio=profile.bio
        )

@strawberry.federation.type(keys=["userId"])
class UserProfile:
    user_id: strawberry.ID
    username: str = strawberry.federation.field(external=True)
    bio: str

Federation Schema SDL

Generate federation-compatible SDL:

from strawberry.printer import print_schema

# Print federation schema
federation_sdl = print_schema(schema)
print(federation_sdl)

Example Output:

extend schema
  @link(url: "https://specs.apollo.dev/federation/v2.0", import: ["@key", "@external", "@requires", "@provides"])

type User @key(fields: "id") {
  id: ID!
  name: String!
  email: String!
  posts: [Post!]!
}

type Post @key(fields: "id") {
  id: ID!
  title: String!
  content: String!
  author: User!
}

type Query {
  user(id: ID!): User
  post(id: ID!): Post
}

Federation Gateway Integration

Services register with Apollo Gateway:

// gateway.js
const { ApolloGateway } = require('@apollo/gateway');
const { ApolloServer } = require('apollo-server');

const gateway = new ApolloGateway({
  serviceList: [
    { name: 'users', url: 'http://localhost:4001/graphql' },
    { name: 'posts', url: 'http://localhost:4002/graphql' },
    { name: 'reviews', url: 'http://localhost:4003/graphql' }
  ]
});

const server = new ApolloServer({
  gateway,
  subscriptions: false
});

This enables clients to make unified queries across all services:

query UnifiedQuery {
  user(id: "1") {
    name
    email
    posts {
      title
      content
      reviews {
        rating
        comment
      }
    }
  }
}

Install with Tessl CLI

npx tessl i tessl/pypi-strawberry-graphql

docs

core-types.md

experimental.md

extensions.md

federation.md

fields-resolvers.md

framework-integrations.md

index.md

relay.md

schema-execution.md

utilities.md

tile.json