Generics
Strawberry supports using Python's Generic
typing to dynamically create
reusable types.
Strawberry will automatically generate the correct GraphQL schema from the combination of the generic type and the type arguments. Generics are supported in Object types, Input types, and Arguments to queries, mutations, and scalars.
Let's take a look at an example:
Object Types
from typing import Generic, List, TypeVar
import strawberry T = TypeVar('T')
@strawberry.typeclass Page(Generic[T]): number: int items: List[T]
This example defines a generic type Page
that can be used to represent a page
of any type. For example, we can create a page of User
objects:
import strawberry
@strawberry.typeclass User: name: str
@strawberry.typeclass Query: users: Page[User]
type Query { users: UserPage!}
type User { name: String!}
type UserPage { number: Int! items: [User!]!}
Input and Argument Types
Arguments to queries and mutations can also be made generic by creating Generic Input types. Here we'll define an input type that can serve as a collection of anything, then create a specialization by using as a filled-in argument on a mutation.
import strawberryfrom typing import Generic, List, Optional, TypeVar T = TypeVar("T")
@strawberry.inputclass CollectionInput(Generic[T]): values: List[T]
@strawberry.inputclass PostInput: name: str
@strawberry.typeclass Post: id: int name: str
@strawberry.typeclass Mutation: @strawberry.mutation def add_posts(self, posts: CollectionInput[PostInput]) -> bool: return True
@strawberry.typeclass Query: most_recent_post: Optional[Post] = None
schema = strawberry.Schema(query=Query, mutation=Mutation)
input PostInputCollectionInput { values: [PostInput!]!}
input PostInput { name: String!}
type Post { id: Int! name: String!}
type Query { mostRecentPost: Post}
type Mutation { addPosts(posts: PostInputCollectionInput!): Boolean!}
Note: Pay attention to the fact that both
CollectionInput
andPostInput
are Input types. Providingposts: CollectionInput[Post]
toadd_posts
(i.e. using the non-inputPost
type) would have resulted in an error:
PostCollectionInput fields cannot be resolved. Input field type must be a GraphQL input type
Multiple Specializations
Using multiple specializations of a Generic type will work as expected. Here we
define a Point2D
type and then specialize it for both int
s and float
s.
from typing import Generic, TypeVar
import strawberry T = TypeVar('T')
@strawberry.inputclass Point2D(Generic[T]): x: T y: T
@strawberry.typeclass Mutation: @strawberry.mutation def store_line_float(self, a: Point2D[float], b: Point2D[float]) -> bool: return True
@strawberry.mutation def store_line_int(self, a: Point2D[int], b: Point2D[int]) -> bool: return True
type Mutation { storeLineFloat(a: FloatPoint2D!, b: FloatPoint2D!): Boolean! storeLineInt(a: IntPoint2D!, b: IntPoint2D!): Boolean!}
input FloatPoint2D { x: Float! y: Float!}
input IntPoint2D { x: Int! y: Int!}
Variadic Generics
Variadic Generics, introduced in PEP-646, are currently unsupported.