Strawberry GraphQL

repository·main·Indexed 11 days ago

https://github.com/strawberry-graphql/strawberry

A Python GraphQL library that leverages dataclasses to define schemas. It includes a built-in CLI, development server, and integrations for frameworks such as Django, Flask, and Sanic. Version 0.324.0 provides support for static type-checking via a mypy plugin and implements the GraphQL multipart request specification for file uploads.

Tokens
116.2K
Snippets
382
Records
451
Agent score
87%

What's inside Strawberry

  1. Introduction to Strawberry Channels integration

    main

    Strawberry provides support for Channels using Consumers. This allows you to provide GraphQL support over both WebSockets and HTTP.

    While Channels requires Django to be installed as a dependency, you can run this integration without using Django's request handler. The most common use case is running a standard Django project with GraphQL subscriptions support, utilizing the Channel Layers functionality exposed through the Strawberry integration.

  2. What is Query Batching in Strawberry?

    main
    Query batching is a feature that allows clients to send multiple GraphQL operations (queries, mutations, or a combination) within a single HTTP request. This optimizes network usage and improves performance by reducing the overhead of multiple individual HTTP requests. When enabled, the server accepts a list of operations and returns a corresponding list of responses.
  3. Understand Cursor-Based Pagination

    main

    Cursor-based pagination (also known as keyset pagination) works by returning a pointer (a "cursor") to a specific item in the dataset. On subsequent requests, the client provides this cursor to fetch results that follow that pointer.

    Strawberry implements cursor-based pagination following the Relay specification.

    Pros:

    • Highly performant for large datasets.
    • Consistent in dynamic environments (less prone to duplicate/skipped items when data changes).

    Cons:

    • Requires a unique, sequential identifier to act as the cursor.
    • Does not provide a total count of items or pages.
    • Clients cannot jump to a specific page (e.g., jumping from page 1 to page 10).
    // Initial request (no cursor known)
    {
      "limit": 2,
      "cursor": null
    }
    
    // Response includes the next cursor
    {
      "users": [...],
      "next_cursor": "3"
    }
    
    // Subsequent request using the returned cursor
    {
      "limit": 2,
      "cursor": "3"
    }
  4. Configure supported subscription protocols

    main

    Strawberry supports multiple subscription protocols. By default, WebSocket protocols are accepted, while Server-Sent Events (SSE) and Multipart subscriptions must be explicitly opted-in.

    Available protocol constants from strawberry.subscriptions:

    • GRAPHQL_TRANSPORT_WS_PROTOCOL: The newer, recommended WebSocket sub-protocol.
    • GRAPHQL_WS_PROTOCOL: The legacy WebSocket sub-protocol (maintained for backwards compatibility).
    • GRAPHQL_SSE_PROTOCOL: Server-Sent Events (SSE).

    You can specify which protocols to accept by passing a list to the subscription_protocols argument in your framework's GraphQL integration (e.g., GraphQLView, GraphQL, GraphQLRouter, or GraphQLProtocolTypeRouter).

  5. Extend AsyncGraphQLView to customize behavior

    main

    The AsyncGraphQLView class can be customized by overriding specific methods to control how requests are processed, how context is provided, and how results are returned. Common extension points include:

    • get_context: Provide custom context for resolvers.
    • get_root_value: Provide a custom root object for the schema.
    • process_result: Transform or log execution results before sending them to the client.
    • decode_json: Use a custom JSON decoder (e.g., orjson).
    • encode_json: Use a custom JSON encoder.
    • render_graphql_ide: Provide a custom HTML template for the GraphQL IDE.
  6. Reuse Object Types with Input Objects

    main
    You cannot reuse strawberry.type (Object Types) as fields within a strawberry.input (Input Objects). This is a limitation of the GraphQL specification, as Object Types can contain fields (like arguments or interfaces) that are invalid for input arguments. When defining Input Objects, you can only use other Strawberry Input types or scalars.
  7. Implement OneOf input types

    main

    To define a OneOf input type (where only one field can be provided), use the one_of=True flag in the @strawberry.input decorator.

    When using one_of, you must use strawberry.Maybe for the fields to correctly distinguish between fields that are explicitly not provided and those that might be set to null.

    import strawberry
    
    @strawberry.input(one_of=True)
    class SearchBy:
        name: strawberry.Maybe[str]
        email: strawberry.Maybe[str]
  8. Use the PaginationWindow type

    main

    The PaginationWindow is a generic Strawberry type used to return a slice of data along with metadata. It is particularly useful for offset-based pagination where the client needs to know the total count of items to calculate the maximum possible offset.

    Fields:

    • items: A list of the requested items (e.g., List[User]).
    • total_items_count: An integer representing the total number of items matching the current filters, used to bound the offset value on the client side.
    {
      users(orderBy: "name", offset: 0, limit: 2) {
        items {
          name
          age
        }
        totalItemsCount
      }
    }
  9. What are Field Extensions and how to use them

    main

    Field extensions allow you to implement reusable logic (like permissions, pagination, or data transformations) outside of your resolvers. They wrap the underlying resolver, allowing you to modify the field or the arguments passed to it.

    To create an extension, subclass strawberry.extensions.FieldExtension and implement the resolve method. The resolve method receives a next_ argument, which is the next function in the chain (either the next extension or the actual resolver).

    Note: The examples below cover synchronous execution. For asynchronous support, see the Async Extensions and Resolvers section.

    import strawberry
    from strawberry.extensions import FieldExtension
    from typing import Callable, Any
    
    class UpperCaseExtension(FieldExtension):
        def resolve(
            self, next_: Callable[..., Any], source: Any, info: strawberry.Info, **kwargs
        ):
            # Call the next resolver/extension in the chain
            result = next_(source, info, **kwargs)
            # Modify the result
            return str(result).upper()
    
    @strawberry.type
    class Query:
        @strawberry.field(extensions=[UpperCaseExtension()])
        def string(self) -> str:
            return "This is a test!!"
  10. What are Interfaces in Strawberry GraphQL

    main

    An Interface is an abstract type that defines a set of fields that multiple object types must implement. Interfaces themselves are never instantiated; instead, they serve as a contract for other object types.

    Use interfaces when you have a set of objects that are used interchangeably and share significant common fields. If the objects do not share common fields, use a Union instead.

    When querying an interface, you can select the common fields directly. To access fields specific to a particular implementation, use GraphQL inline fragments (... on TypeName).

    query {
      customers {
        name
        ... on Individual {
          employed_by {
            name
          }
        }
      }
    }
  11. Define a GraphQL Schema with strawberry.Schema

    main

    A GraphQL schema is defined by providing root types for Query, Mutation, and Subscription.

    • Query is required.
    • Mutation and Subscription are optional.

    You instantiate a schema using strawberry.Schema(query=Query, mutation=Mutation, subscription=Subscription).

    import strawberry
    
    @strawberry.type
    class Query:
        @strawberry.field
        def hello(self) -> str:
            return "Hello World"
    
    schema = strawberry.Schema(Query)
  12. Use `strawberry.Maybe` to differentiate between null and absent fields

    main

    In GraphQL, there is a distinction between a field being null and a field being completely absent from an input. strawberry.Maybe allows you to handle these three states in your Python code, which is essential for update mutations (PATCH operations).

    The Three States

    Depending on how you define the type, you can represent different combinations of presence and nullability:

    1. Maybe[T]:

      • Field present with a value: Some(value)
      • Field completely absent: None
      • Note: Sending null will trigger a validation error.
    2. Maybe[T | None]:

      • Field present with a value: Some(value)
      • Field present but explicitly null: Some(None)
      • Field completely absent: None

    Accessing the value

    To access the underlying data, you must first check if the field was provided (is not None) and then access the .value attribute of the Some container.

    import strawberry
    
    @strawberry.input
    class UpdateUserInput:
        phone: strawberry.Maybe[str | None]
    
    @strawberry.type
    class Mutation:
        @strawberry.mutation
        def update_user(self, input: UpdateUserInput) -> User:
            # 1. Check if the field was provided at all
            if input.phone is not None:
                # 2. Access the actual value (which could be a string or None)
                user.phone = input.phone.value
            # If input.phone is None, the field was absent; do nothing.