NewYour coding agent can read the release notes before it upgrades.Set up the MCP server →
PyPI · #1573 most downloaded on PyPI
A library for creating GraphQL APIs
Last release today
04 Oct 2026
Ships fairly regularly
a new release about every 3 weeks
Nearly every release is documented
notes for 57 of the last 60 stable releases
Nothing withdrawn
no release was ever pulled
8 years old
1125 releases · first in 2019
Nothing published for this version
Nothing published for this version
Nothing published for this version
This release fixes types that inherit an interface through a class that isn't decorated with Strawberry. Strawberry listed the interface more than onc
This release fixes types that inherit an interface through a class that isn't
decorated with Strawberry. Strawberry listed the interface more than once for
these types, so building the schema failed with errors like
Type Fruit can only implement Node once.
This happened, for example, when sharing resolve_nodes between relay.Node
types with a base class:
import strawberry
from strawberry import relay
class DatabaseNode(relay.Node):
@classmethod
def resolve_nodes(cls, *, info, node_ids, required=False):
return [load(cls, node_id) for node_id in node_ids]
@strawberry.type
class Fruit(DatabaseNode):
id: relay.NodeID[int]
name: strFruit now implements Node once. The same applies to interfaces that extend
another interface through an undecorated class, and the error raised when an
input inherits an interface this way now lists the interface once.
A class like DatabaseNode can share methods such as resolve_nodes, but
Strawberry ignores fields declared on it. To share fields too, decorate it with
@strawberry.interface or @strawberry.type.
This release was contributed by @patrick91 in #4661
One column per quarter.
This release fixes list defaults of arguments and input fields being shared between requests.
This release fixes list defaults of arguments and input fields being shared
between requests.
When a client omitted an argument or input field whose default is a list of
scalars or enums, every request got the same list. A resolver that changed it,
for example by appending to it, changed the default for all the following
requests, and the default printed in the schema too:
import strawberry
@strawberry.input
class Filter:
tags: list[str] = strawberry.field(default_factory=lambda: ["base"])
@strawberry.type
class Query:
@strawberry.field
def search(self, filter: Filter) -> list[str]:
filter.tags.append("added")
return filter.tagsEach { search(filter: {}) } request now returns ["base", "added"], instead
of one more "added" than the previous one. Strawberry copies these lists again
when it converts the arguments, like it did before 0.275.5, which also covers
nested and optional lists, lists in input type defaults and directive arguments.
This release was contributed by @patrick91 in #4660
This release adds a to_input hook to StrawberryObjectDefinition , the counterpart of from_input . Like from_input , it's meant for integrations that c
This release adds a to_input hook to StrawberryObjectDefinition, the
counterpart of from_input. Like from_input, it's meant for integrations that
create their own type definitions, which pass it when they create the
definition. It isn't meant to be set on types defined with @strawberry.input.
Strawberry converts input instances used as argument and input field defaults to
GraphQL input values when it builds the schema. When the input type sets
to_input, Strawberry calls it to get the instance's fields, instead of using
all of them. For example, an integration for Pydantic models can use it to only
include the fields that were set, so that the default of
patch: UserPatch = UserPatch(name="Ada") is { name: "Ada" }, instead of
setting every other field to None:
from typing import Any
from pydantic import BaseModel
from strawberry.types.base import StrawberryObjectDefinition
def dump_model(model: BaseModel) -> dict[str, Any]:
return {name: getattr(model, name) for name in model.model_fields_set}
definition = StrawberryObjectDefinition(
name="UserPatch",
is_input=True,
# the other arguments of the definition
to_input=dump_model,
)The hook returns the values of the fields, keyed by their Python names. Every
field it returns is part of the default, with None as an explicit null, unless
its value is UNSET, and nested input instances use their own type's
to_input. The fields it leaves out aren't part of the default.
This release was contributed by @patrick91 in #4658
This release adds a from_input hook to StrawberryObjectDefinition , for integrations that create their own type definitions, like the first-class Pyda
This release adds a from_input hook to StrawberryObjectDefinition, for
integrations that create their own type definitions, like the first-class
Pydantic integration. It lets them build input values their own way, for example
to validate a whole input with the request's info. It isn't meant to be set on
types defined with @strawberry.input.
An integration passes the hook when it creates the type definition. Strawberry
then calls it with the class to build, the input value keyed by GraphQL field
names, and an InputContext, instead of converting the fields and passing them
to the class:
from collections.abc import Mapping
from typing import Any
from strawberry.types.arguments import InputContext
from strawberry.types.base import StrawberryObjectDefinition
def build_model(cls: type, value: Mapping[str, Any], context: InputContext) -> Any:
return cls.model_validate(value, context={"info": context.info})
definition = StrawberryObjectDefinition(
name="UserInput",
is_input=True,
# the other arguments of the definition
from_input=build_model,
)InputContext has the request's info, the schema config and scalar registry,
the path of the value in the arguments, made of GraphQL field names and list
indices, like ("input", "items", 0), and a convert() method that converts
nested values like Strawberry does, given a Strawberry type or an annotation
like list[Item]. Its keys are the location of the value in the input, so inputs
nested in it know their own location:
context.convert(value["items"][0], Item, "items", 0)The hook is used for field arguments, directive arguments and federation
entities, and for argument and input field defaults, which Strawberry converts
to input values when it builds the schema. Federation entities have the location
of their representation, like ("representations", 0), and GraphQL errors
raised while building them are now returned to the client, instead of a generic
"Unable to resolve reference" error.
This release was contributed by @patrick91 in #4655
This release fixes argument and input field defaults that are instances of an input type:
This release fixes argument and input field defaults that are instances of an
input type:
@strawberry.input
class Filter:
limit: int = 10
@strawberry.type
class Query:
@strawberry.field
def items(self, filter: Filter = Filter(limit=5)) -> list[Item]: ...Executing a field that used such a default failed with
argument of type 'Filter' is not iterable, and introspection queries failed
with Invalid default value. The same happened for input fields whose
default_factory returns an input instance.
Strawberry now converts these defaults to GraphQL input values when it builds
the schema, so introspection shows them, and resolvers get an instance built from
the default for every request, like when the client sends the value. Defaults
given as dicts keyed by Python names, like {"page_size": 5}, and GlobalID
defaults are converted too, instead of being ignored or failing when the field
is executed.
Printed and introspected defaults leave out None values that are the field's
default, and keep an explicit null only when it changes the value, for example
for a field whose default isn't None.
This release was contributed by @patrick91 in #4654
Additional contributors: @bellini666
This release fixes objects cast with strawberry.cast not being resolved when they're returned in a union.
This release fixes objects cast with strawberry.cast not being resolved when
they're returned in a union.
Resolvers can return objects that aren't instances of a union's types, like rows
of an ORM, by casting them to the type they represent:
@strawberry.type
class Query:
@strawberry.field
def latest_media(self) -> Audio | Video:
return strawberry.cast(Video, db.media.latest())Casts to a generic type work too, like strawberry.cast(Edge, row) for an
Edge[int] member, also when it's returned through an interface that Edge
implements. When more than one possible type is made from the generic class,
like Edge[int] | Edge[str], the cast is ambiguous and Strawberry raises an
error, in unions and interfaces alike.
The cast takes precedence over the types' is_type_of, and a cast that matches
none of the possible types, for example a cast to an interface, is ignored, so
the object is resolved as before.
This release was contributed by @patrick91 in #4653
This release fixes unions with the same name but different types.
This release fixes unions with the same name but different types.
Strawberry reused a union by its name without checking its types, so a union
whose generated name matched another union, for example Ok | Error with two
different Error types, was silently replaced by the first one, and returning
the second Error failed at runtime:
@strawberry.type
class Query:
@strawberry.field
def a(self) -> Ok | Error: ...
@strawberry.field
def b(self) -> Ok | OtherError: ... # also named OkErrorBuilding this schema now raises DuplicatedTypeName, like other types with the
same name do. Explicitly named unions, such as
Annotated[A | B, strawberry.union("AB")], are checked too.
This release was contributed by @patrick91 in #4657
This release adds support for graphql-core 3.3.0 and drops support for graphql-core 3.2 and the 3.3 pre-releases. See the breaking changes page for up…
This release adds support for graphql-core 3.3.0 and drops support for
graphql-core 3.2 and the 3.3 pre-releases. See the
breaking changes page
for upgrade notes.
It also fixes:
@defer and @stream returning a single, non-incremental result on@defer and @stream in synchronous execution (Schema.execute_sync andAnnotated["User", strawberry.lazy(...)]) underfrom __future__ import annotations failing to resolve in some cases.info.selected_fields crashing on inline fragments without a type condition.This release was contributed by @patrick91 in #4641
This release fixes unnecessary overhead when handling resolver results.
This release fixes unnecessary overhead when handling resolver results.
Strawberry now does a little less work to check whether a result needs to be
awaited. This small optimization works automatically with your existing
synchronous and asynchronous resolvers, with no code changes needed.
This release was contributed by @patrick91 in #4618
This release fixes unnecessary whitespace in responses produced by Strawberry's shared default JSON encoders, reducing their size without changing dec
This release fixes unnecessary whitespace in responses produced by Strawberry's
shared default JSON encoders, reducing their size without changing decoded data.
The encoders now omit spaces after commas and colons. This also applies to
WebSocket messages and SSE/multipart JSON payloads that use these encoders;
protocol framing is unchanged. Unicode escaping, Django's DjangoJSONEncoder,
and custom encode_json overrides retain their existing behavior.
Channels' ordinary HTTP responses still use their separate serialization path
and are unaffected by this change.
This release was contributed by @patrick91 in #4612
This release fixes Channels HTTP responses bypassing custom encode_json overrides.
This release fixes Channels HTTP responses bypassing custom encode_json
overrides.
Both GraphQLHTTPConsumer and SyncGraphQLHTTPConsumer now use the encoding hook
for single and batched JSON responses, including GraphQL errors. Bytes are sent
unchanged, while strings are encoded as UTF-8.
This release was contributed by @patrick91 in #4613
This release fixes unnecessary allocation and enqueue overhead in DataLoader.
This release fixes unnecessary allocation and enqueue overhead in DataLoader.
Batch entries now use slotted dataclasses and avoid runtime generic construction.
Batch selection also avoids repeated attribute lookups and length-method calls.
Existing batching, caching, priming, and cancellation behavior is preserved.
This release was contributed by @patrick91 in #4611
This release fixes classification of built-in scalar and OneOf input errors.
This release fixes classification of built-in scalar and OneOf input errors.
Strawberry now raises StrawberryInputCoercionError for these client input
errors, allowing server-side error handling and monitoring to distinguish them
from server faults without changing error messages or serialized responses.
Custom scalar parsers can raise the same exception for expected conversion
errors.
This release was contributed by @patrick91 in #4593
Additional contributors: @ampagent
This release fixes the legacy graphql-ws protocol handler so that subscriptions which complete on their own (or fail before execution) release their s
This release fixes the legacy graphql-ws protocol handler so that
subscriptions which complete on their own (or fail before execution) release
their slot on the connection.
Previously, completed operations were kept in the handler's bookkeeping until
the client sent a stop message for them, reused their operation id, or
disconnected. On connections with max_subscriptions_per_connection
configured, a client using distinct operation ids could therefore hit
Subscription limit reached even though none of its earlier subscriptions
were still active. The graphql-transport-ws handler was not affected.
Sending a stop message for an operation that has already completed is now a
no-op instead of an error.
This release was contributed by @patrick91 in #4610
This release adds continuous compatibility testing against the latest Strawberry Django release.
This release adds continuous compatibility testing against the latest Strawberry
Django release.
Strawberry pull requests now run the complete Strawberry Django test suite using
the proposed Strawberry changes.
This release was contributed by @patrick91 in #4608
Additional contributors: @amp
This release fixes a potential unbounded memory growth in the ParserCache and ValidationCache extensions.
This release fixes a potential unbounded memory growth in the ParserCache and
ValidationCache extensions.
Both extensions previously defaulted to maxsize=None, which creates an
unbounded functools.lru_cache. On a network-exposed endpoint with one of these
extensions enabled, a client sending many distinct query texts could grow the
server's memory without limit.
The default is now a bounded LRU cache of 128 entries, matching the
functools.lru_cache default. Existing behavior can be restored by explicitly
opting in to an unbounded cache:
import strawberry
from strawberry.extensions import ParserCache, ValidationCache
schema = strawberry.Schema(
Query,
extensions=[
ParserCache(maxsize=None), # explicitly unbounded
ValidationCache(maxsize=100),
],
)Only use maxsize=None when the set of distinct query texts reaching the server
is trusted and bounded.
This release was contributed by @patrick91 in #4606
This release fixes a permission bypass ( GHSA-pfvf-fwfp-25mp ) where a custom permission could unintentionally authorize access to a protected field.
This release fixes a permission bypass (GHSA-pfvf-fwfp-25mp) where a custom
permission could unintentionally authorize access to a protected field.
When a permission's has_permission was a normal def that returned an
awaitable (for example a wrapper returning a coroutine), Strawberry classified
the permission as synchronous because only async def methods are detected as
async. On the synchronous resolve path the returned awaitable was evaluated for
truthiness directly, and an awaitable is always truthy — so the check passed and
the protected resolver ran even when the awaitable resolved to False. This
affected any field with a synchronous resolver, under both execute_sync and
execute.
Strawberry now detects this case and fails closed: the synchronous permission
path raises a clear error instead of trusting the awaitable, so access is never
granted by accident. Permissions written as async def has_permission continue
to work as before. If you intend a permission to be asynchronous, declare it
with async def (or return a plain boolean from a synchronous one).
This release was contributed by @patrick91 in #4605
This release fixes introspection for custom schema directives.
This release fixes introspection for custom schema directives.
Schema directives attached to types, fields, arguments, and other schema elements
now appear in standard GraphQL introspection. Schema explorers, IDEs, code
generators, and other tools can discover each directive's description, arguments,
allowed locations, repeatability, and any input types it uses. Federation directives,
including generated @link and @composeDirective applications, are discoverable
in the same way.
Federation directives and custom composed directives used on field arguments are
also included in the generated subgraph metadata, so routers can recognize those
argument annotations without additional schema configuration.
A directive reused across the schema is defined only once. Input, enum, and scalar
types referenced by directive arguments are now part of the schema and may appear
in generated SDL even when they are not used by fields.
Because these directives and argument types are now part of the runtime schema,
their GraphQL names must be unique. Schema construction reports a clear error when
different directive definitions share a name, a custom directive replaces a
built-in directive such as @skip, or a directive argument type conflicts with
another schema type. Compatible custom @oneOf definitions continue to use
GraphQL's built-in directive. Strawberry now also resolves attached directive
argument annotations during schema construction, so unresolved forward references
are reported when the schema is created instead of later when its SDL is printed.
This release was contributed by @patrick91 in #4598
This release adds richer verbose output to assert_no_errors . When GraphQL errors are detected, the assertion now includes full error details, making
This release adds richer verbose output to assert_no_errors. When GraphQL
errors are detected, the assertion now includes full error details, making it
easier to debug failing tests.
This release was contributed by @Akay7 in #4423
Additional contributors: @greptile-apps[bot], @sourcery-ai[bot], @bellini666, @pre-commit-ci[bot]
This release fixes incorrect dataclass transform ordering metadata.
This release fixes incorrect dataclass transform ordering metadata.
Strawberry decorators now correctly declare that ordering methods are not generated
by default, matching their runtime dataclass behavior and allowing custom ordering
methods such as __gt__ to be used without type-checking errors.
This release was contributed by @subham-hq in #4591
This release fixes argument handling for operation directive resolvers.
This release fixes argument handling for operation directive resolvers.
Arguments passed to operation directives now use GraphQL's standard coercion before
your resolver runs. Directive resolvers receive Python numeric values, Strawberry
enum members and nested input objects, and values parsed by custom scalars, whether
clients use literals or variables.
When a client omits a variable, Strawberry now applies the directive argument's
default. Explicit null continues to reach nullable arguments as None.
This release was contributed by @patrick91 in #4596
This release fixes silently ignored strawberry.field() metadata in nested type annotations.
This release fixes silently ignored strawberry.field() metadata in nested type
annotations.
Strawberry now raises a clear error when field metadata is placed below the
class-field annotation, such as on a list item, and explains that it must be moved
to the outermost Annotated metadata for the field.
For example, Strawberry now reports this misplaced metadata:
from typing import Annotated
import strawberry
@strawberry.type
class Query:
names: list[Annotated[str, strawberry.field(description="A name")]]Move strawberry.field() to the field's outermost Annotated metadata:
@strawberry.type
class Query:
names: Annotated[list[str], strawberry.field(description="The names")]This release was contributed by @patrick91 in #4595
This release fixes fields configured with strawberry.field() inside typing.Annotated .
This release fixes fields configured with strawberry.field() inside
typing.Annotated.
You can now use this syntax consistently on object types, input types, and
interfaces, including in projects that use from __future__ import annotations:
from typing import Annotated
import strawberry
Name = Annotated[
str,
strawberry.field(name="displayName", default="Anonymous"),
]
@strawberry.type
class User:
name: NameAll strawberry.field() options are supported. Fields with default or
default_factory can be omitted when creating an instance, and field
configuration can be combined with other Strawberry metadata such as named
unions.
This release was contributed by @patrick91 in #4594
This release adds support for the upcoming Python 3.15.
This release adds support for the upcoming Python 3.15.
Strawberry's test suite now runs on Python 3.15.
This release was contributed by @patrick91 in #4565
…or newer: unmaintained releases no longer get security fixes.
This release adds support for Django 6.0 and 6.1, and drops support for Django
older than 5.2.
Django 4.2, 5.0 and 5.1 have all reached end of life. Django 5.2 LTS is now the
minimum supported version, and the test suite runs against Django 5.2, 6.0 and
6.1.
If you are still on one of the dropped versions, we strongly recommend
upgrading to Django 5.2 or newer: unmaintained releases no longer get security
fixes.
This release was contributed by @bellini666 in #4576
This release fixes an issue where MaskErrors leaked parsing and validation error details during synchronous execution.
This release fixes an issue where MaskErrors leaked parsing and validation
error details during synchronous execution.
Synchronous execution now masks pre-execution errors consistently with
asynchronous execution, including when ValidationCache is enabled.
This release was contributed by @dextermb in #3968
Additional contributors: @patrick91
This release fixes type checking for FastAPI GraphQLRouter subclasses that provide a custom context getter without explicit generic parameters.
This release fixes type checking for FastAPI GraphQLRouter subclasses that
provide a custom context getter without explicit generic parameters.
Bare subclasses now default to the context types supported by the FastAPI
integration, so valid context getters are accepted by type checkers. Explicitly
using GraphQLRouter[MyContext] is still available when the precise context
type needs to be preserved on the subclass.
The minimum supported typing-extensions version is now 4.14.0, ensuring these
default generic parameters work across Strawberry's supported Python versions.
This release was contributed by @patrick91 in #4540
This release adds an on_stream_result hook to SchemaExtension for extension authors who need to inspect or mutate GraphQL results before they reach a
This release adds an on_stream_result hook to SchemaExtension for extension
authors who need to inspect or mutate GraphQL results before they reach a
streaming transport.
The hook wraps subscription events and queries or mutations sent over WebSockets,
SSE, or multipart responses. On transports that support experimental incremental
execution, it also wraps each incremental-delivery frame.
Strawberry's built-in MaskErrors extension now uses the hook so streamed query,
mutation, subscription, incremental-delivery, and pre-execution errors are masked
before being sent to clients.
This release was contributed by @Ladol in #4330
Additional contributors: @pre-commit-ci[bot], @patrick91
This release fixes a bug in Relay connection pagination where combining first with before returned the wrong slice of items — walking backward from th
This release fixes a bug in Relay connection pagination where combining first with before returned the wrong slice of items — walking backward from the before cursor instead of taking the first first items among those before it, per the Relay Cursor Connections spec.
This release fixes the built-in UUID , Date , DateTime , and Time scalars to reject non-string variable values with a standard coercion error instead
This release fixes the built-in UUID, Date, DateTime, and Time scalars
to reject non-string variable values with a standard coercion error instead of
raising an unhandled AttributeError/TypeError inside the parser.
Previously a value like {"id": 469610.0} sent into a UUID position crashed
with 'float' object has no attribute 'replace' and surfaced in error
trackers as a server-side exception. The Decimal scalar keeps accepting
numeric input by stringifying it, as before.
This release was contributed by @simonline in #4525
This release adds a cleaner extension API by removing the deprecated Extension import alias from strawberry.extensions . The alias was deprecated in 0…
This release adds a cleaner extension API by removing the deprecated Extension
import alias from strawberry.extensions. The alias was deprecated in
0.160.0;
import SchemaExtension instead.
Before (deprecated):
from strawberry.extensions import Extension
class MyExtension(Extension): ...After:
from strawberry.extensions import SchemaExtension
class MyExtension(SchemaExtension): ...This release was contributed by @Ckk3 in #4210
Additional contributors: @patrick91, @github-actions[bot]
This release adds precise type annotations for custom scalars: serialize , parse_value and parse_literal are now typed with graphql-core's GraphQLScal
This release adds precise type annotations for custom scalars: serialize,
parse_value and parse_literal are now typed with graphql-core's
GraphQLScalarSerializer, GraphQLScalarValueParser and
GraphQLScalarLiteralParser aliases instead of bare Callable, so
strawberry.scalar(...) calls type-check cleanly under strict type checkers.
This release was contributed by @Flamefork in #4527
This release adds configurable exception handlers that map Python exceptions to typed GraphQL union results.
This release adds configurable exception handlers that map Python exceptions to
typed GraphQL union results.
Most applications do not need to adopt this directly. It is primarily useful for
integrations and framework-level helpers: for example, catching validation
exceptions from a library such as Pydantic and exposing them as an explicit
GraphQL error type, without requiring every resolver to catch and convert those
exceptions manually.
Handlers are passed to strawberry.Schema (and strawberry.federation.Schema):
import strawberry
from strawberry.types.field import StrawberryField
class ValidationProblem(Exception):
pass
@strawberry.type
class ValidationError:
message: str
class ValidationErrorHandler(
strawberry.ExceptionHandler[ValidationProblem, ValidationError]
):
def handle(
self,
exception: ValidationProblem,
*,
field: StrawberryField,
info: strawberry.Info,
) -> ValidationError:
return ValidationError(message=str(exception))
schema = strawberry.Schema(
query=Query,
mutation=Mutation,
exception_handlers=[ValidationErrorHandler()],
)Handlers can alternatively declare exception_type and error_type class
attributes instead of type parameters, which also covers types that are only
known at runtime. Declaring both a type parameter and a conflicting attribute
for the same slot raises an error at schema creation.
Strawberry only converts the exception when the field return type includes the
handler's GraphQL error type. Other fields continue to raise normal GraphQL
errors, so applications can opt in one field at a time. Exceptions raised by
the resolver, during argument conversion, or by field extensions are all
covered.
Exception handlers apply to query and mutation fields. Subscriptions are not
covered: exceptions raised while establishing a subscription are not converted
into union results.
This release was contributed by @patrick91 in #4492
This release fixes a PyCharm false positive where classes decorated with @strawberry.type and @strawberry.input reported "Unexpected argument" on thei
This release fixes a PyCharm false positive where classes decorated with @strawberry.type and @strawberry.input reported "Unexpected argument" on their generated keyword constructors.
This release was contributed by @Speedy1991 in #4508
Additional contributors: @github-actions[bot], @patrick91
This release fixes InputMutationExtension to unpack its generated input object into the resolver's individual keyword arguments before permission clas
This release fixes InputMutationExtension to unpack its generated input
object into the resolver's individual keyword arguments before permission
classes and other field extensions run.
Previously, permission classes and field extensions on an input-mutation field
received the wrapping input object; they now receive the individual arguments
(for example name and color).
import strawberry
from strawberry.field_extensions import InputMutationExtension
from strawberry.permission import BasePermission
class IsAuthenticated(BasePermission):
message = "Not authenticated"
def has_permission(self, source, info, **kwargs) -> bool:
# Previously: kwargs == {"input": CreateFruitInput(name=..., color=...)}
# Now: kwargs == {"name": ..., "color": ...}
return True
@strawberry.type
class Mutation:
@strawberry.mutation(
extensions=[InputMutationExtension()],
permission_classes=[IsAuthenticated],
)
def create_fruit(self, name: str, color: str) -> Fruit:
return Fruit(name=name, color=color)
This release was contributed by @patrick91 in https://github.com/strawberry-graphql/strawberry/pull/4510
Additional contributors: @github-actions[bot]
This release fixes a bug where print_schema would emit a type definition twice when the same type (for example an enum) was referenced both as a schem
This release fixes a bug where print_schema would emit a type definition
twice when the same type (for example an enum) was referenced both as a schema
directive field and elsewhere in the schema. The duplicated definition produced
invalid SDL that violates the GraphQL spec and is rejected by graphql-core's
build_schema.
For example, the following schema now prints enum Role only once:
import enum
import strawberry
from strawberry.schema_directive import Location
@strawberry.enum
class Role(enum.Enum):
EDITOR = "editor"
VIEWER = "viewer"
@strawberry.schema_directive(locations=[Location.FIELD_DEFINITION])
class RequiresRole:
roles: list[Role]
@strawberry.type
class Query:
secret: str = strawberry.field(directives=[RequiresRole(roles=[Role.EDITOR])])
@strawberry.field
def assign(self, role: Role) -> bool:
return True
This release was contributed by @patrick91 in https://github.com/strawberry-graphql/strawberry/pull/4504
This release fixes a bug in the Datadog extension where spans could be left open indefinitely when a GraphQL operation raised an exception.
This release fixes a bug in the Datadog extension where spans could be left open indefinitely when a GraphQL operation raised an exception.
Strawberry now makes sure that the Datadog extension always closes spans, also in the case of exceptions.
This release was contributed by @tsauerwein in https://github.com/strawberry-graphql/strawberry/pull/4498
Additional contributors: @github-actions[bot], @patrick91
Breaking change: multipart subscriptions are now opt-in. Streaming transports are selected from subscription_protocols, so multipart subscriptions — p…
This release adds support for GraphQL subscriptions over Server-Sent Events (SSE), following the GraphQL over SSE protocol in "distinct connections mode".
SSE is opt-in. Enable it by including GRAPHQL_SSE_PROTOCOL in your
integration's subscription_protocols:
from strawberry.asgi import GraphQL
from strawberry.subscriptions import (
GRAPHQL_SSE_PROTOCOL,
GRAPHQL_TRANSPORT_WS_PROTOCOL,
GRAPHQL_WS_PROTOCOL,
)
app = GraphQL(
schema,
subscription_protocols=[
GRAPHQL_TRANSPORT_WS_PROTOCOL,
GRAPHQL_WS_PROTOCOL,
GRAPHQL_SSE_PROTOCOL,
],
)
Clients request a stream with Accept: text/event-stream. Queries, mutations,
subscriptions, and @defer/@stream are all supported on the async
streaming-capable integrations (ASGI, FastAPI, AIOHTTP, Litestar, Quart, Sanic,
async Django, and async Channels). See the
SSE subscriptions docs
for client setup, reconnection, and deployment notes (prefer HTTP/2).
Breaking change: multipart subscriptions are now opt-in. Streaming
transports are selected from subscription_protocols, so multipart
subscriptions — previously served for any Accept: multipart/mixed request —
now require MULTIPART_SUBSCRIPTION_PROTOCOL to be listed there explicitly.
This release was contributed by @patrick91 in https://github.com/strawberry-graphql/strawberry/pull/4466
This release adds Schema.stream(...), a shared execution API for custom streaming integrations.
This release adds Schema.stream(...), a shared execution API for custom
streaming integrations.
Schema.stream(...) accepts queries, mutations and subscriptions and always
returns an async sequence of results: a single ExecutionResult for queries and
mutations, and the stream of results for subscriptions. This lets streaming
transports use one schema entry point instead of choosing between execute and
subscribe themselves.
Incremental delivery operations (@defer/@stream) are also supported: their
initial result is yielded first, followed by each raw graphql-core patch frame,
which the transport is responsible for formatting.
The schema execution APIs accept either a string or an already-parsed
DocumentNode, so a transport that parsed the document itself (for example to
inspect the operation type before executing) can pass the node to avoid parsing
it again.
Strawberry's multipart HTTP transport and graphql-transport-ws handler now use
this path internally.
HTTP multipart response framing now lives in the stream transport layer instead
of AsyncBaseHTTPView.encode_multipart_data. Integrations that customized that
view method should customize the transport encoding instead.
This release was contributed by @patrick91 in https://github.com/strawberry-graphql/strawberry/pull/4460
This release fixes compatibility with graphql-core 3.3 release candidates.
This release fixes compatibility with graphql-core 3.3 release candidates.
Strawberry now handles graphql-core's renamed custom executor hook and nullable
AST argument and directive collections, so schemas using custom execution
contexts and Info.selected_fields continue to work when testing against
graphql-core 3.3.
This release was contributed by @patrick91 in https://github.com/strawberry-graphql/strawberry/pull/4470
This release adds a shared internal HTTP stream transport for multipart subscription responses.
This release adds a shared internal HTTP stream transport for multipart subscription responses.
Application behavior for multipart subscriptions is largely unchanged. The shared transport now owns multipart response headers, heartbeat frames, completion frames, batching errors, and sync-mode errors, keeping Strawberry's built-in HTTP integrations on the same streaming contract.
The one behavioral change is that each multipart part's Content-Length is now
computed from the UTF-8 byte length of the payload instead of its character
count, fixing the header for responses containing non-ASCII data.
This release was contributed by @patrick91 in https://github.com/strawberry-graphql/strawberry/pull/4469
This release fixes schema printing for input object defaults that explicitly set nullable fields to null.
This release fixes schema printing for input object defaults that explicitly set
nullable fields to null.
Strawberry now preserves explicit None values in printed nested input
defaults, including fields set with strawberry.Some(None) and fields renamed
with strawberry.field(name=...), while still omitting fields that were not
explicitly set.
This release was contributed by @patrick91 in https://github.com/strawberry-graphql/strawberry/pull/4461
Previously attributes which were not camelCase would be removed from field arguments for queries: ```python import strawberry
Previously attributes which were not camelCase would be removed from field arguments for queries:
import strawberry
@strawberry.input
class Bar:
a: str
b: str
some_values: list[str] | None = None
@strawberry.input(one_of=True)
class Foo:
bar: strawberry.Maybe[Bar]
c: strawberry.Maybe[str]
@strawberry.type
class Query:
@strawberry.field
async def foobar(
self,
info: strawberry.Info,
foo: Foo = Foo(
bar=strawberry.Some(Bar(a="hi", b="bye", some_values=["my", "world"])),
c=None,
),
) -> str: ...
schema = strawberry.Schema(Query)
Once printed the output would look like the following with the some_values removed.
directive @oneOf on INPUT_OBJECT
input Bar {
a: String!
b: String!
someValues: [String!] = null
}
input Foo @oneOf {
bar: Bar
c: String
}
type Query {
foobar(foo: Foo! = {bar: {a: "hi", b: "bye"}}): String!
}
After this fix, the value will be reflected in the print statement:
directive @oneOf on INPUT_OBJECT
input Bar {
a: String!
b: String!
someValues: [String!] = null
}
input Foo @oneOf {
bar: Bar
c: String
}
type Query {
foobar(foo: Foo! = {bar: {a: "hi", b: "bye", someValues: ["my", "world"]}}): String!
}
This release was contributed by @plyte in https://github.com/strawberry-graphql/strawberry/pull/4391
Additional contributors: @pre-commit-ci[bot]
scalar_overrides now matches generic dict annotations by their origin as a fallback, so registering a bare dict covers every dict[K, V] parameterizati
scalar_overrides now matches generic dict annotations by their origin as a
fallback, so registering a bare dict covers every dict[K, V]
parameterization instead of needing one entry per variant.
Before, registering only the unparameterized origin (dict) did not match
parameterized annotations like dict[str, int]. You had to register the exact
annotation, and any other shape such as dict[str, list[int]] raised
Unexpected type:
from typing import Any
import strawberry
from strawberry.scalars import JSON
@strawberry.type
class Query:
settings: dict[str, Any]
metadata: dict[str, list[int]]
schema = strawberry.Schema(
query=Query,
scalar_overrides={dict[str, Any]: JSON},
)
Now registering the unparameterized dict covers all of its parameterizations,
including resolver and mutation arguments typed with dict[...]. This is
especially useful for strawberry.experimental.pydantic models with typed
dictionary fields. Lookup still tries an exact match first, so existing overrides
keep working unchanged.
This release was contributed by @Vansh-Sharma27 in https://github.com/strawberry-graphql/strawberry/pull/4440
Additional contributors: @pre-commit-ci[bot]
Passing an instance is now deprecated. extensions no longer accepts SchemaExtension instances in the type signature, so mypy and pyright will flag exi…
Schema(extensions=...) now accepts a class or a zero-arg factory and
builds a fresh extension per request. This fixes a race where shared
instances leaked ExecutionContext across concurrent requests, which
also produced mixed traces in ApolloTracingExtension,
ApolloFederationTracingExtension, DatadogTracingExtension, and
OpenTelemetryExtension.
schema = strawberry.Schema(
Query,
extensions=[
MaxTokensLimiter,
lambda: MyExtension(arg=...),
],
)
Passing an instance is now deprecated. extensions no longer accepts
SchemaExtension instances in the type signature, so mypy and pyright
will flag existing call sites. Instances still work at runtime for
backwards compatibility, but the same object is reused for every
request, so concurrent requests can see each other's
ExecutionContext. Switch to the class or a factory for per-request
isolation and to silence the warning.
This release was contributed by @bellini666 in https://github.com/strawberry-graphql/strawberry/pull/4392
This release fixes validation of fragment spreads in QueryDepthLimiter and MaxAliasesLimiter.
This release fixes validation of fragment spreads in QueryDepthLimiter and
MaxAliasesLimiter.
QueryDepthLimiter now tracks visited fragments while calculating operation depth,
preventing circular fragment references from causing unbounded recursion.
MaxAliasesLimiter now expands fragment spreads when counting aliases, so aliases
declared inside fragments are counted each time the fragment is used.
This release was contributed by @patrick91 in https://github.com/strawberry-graphql/strawberry/pull/4421
Fixes these two CVEs:
Fix UnresolvedFieldTypeError when using from __future__ import annotations together with a module-level Annotated[..., strawberry.lazy(...)] alias. Th
Fix UnresolvedFieldTypeError when using from __future__ import annotations together with a module-level Annotated[..., strawberry.lazy(...)] alias. The aliased form now resolves the same as inlining Annotated[...] on the field.
from __future__ import annotations
from typing import TYPE_CHECKING, Annotated
import strawberry
if TYPE_CHECKING:
from .user import User
LazyUser = Annotated["User", strawberry.lazy(".user")]
@strawberry.type
class Post:
user: LazyUser # previously failed
Contributed by @bellini666 in #4415
The types.field.resolver.ReservedParameterSpecification protocol now requires classes to have __hash__ as well, because instances of those classes are
The types.field.resolver.ReservedParameterSpecification protocol now requires classes to have __hash__ as well,
because instances of those classes are to be used as dictionary keys.
This release was contributed by @jonathandung in https://github.com/strawberry-graphql/strawberry/pull/4411
Additional contributors: @pre-commit-ci[bot]
This release fixes an issue in the bundled GraphiQL template where editing HTTP headers, including Authorization headers, wrote those values into the
This release fixes an issue in the bundled GraphiQL template where editing HTTP
headers, including Authorization headers, wrote those values into the browser
URL.
GraphiQL still supports loading headers from existing headers URL parameters,
but newly edited headers are no longer added to the URL. Query and variables URL
sharing is unchanged.
This release was contributed by @patrick91 in https://github.com/strawberry-graphql/strawberry/pull/4409
This release fixes the Channels integration when using cross-web 0.6.0 or newer.
This release fixes the Channels integration when using cross-web 0.6.0 or newer.
cross-web now requires request adapters to expose path_params, so Strawberry's
Channels HTTP adapters now read them from the Channels url_route scope.
This release was contributed by @patrick91 in https://github.com/strawberry-graphql/strawberry/pull/4390
Await cancelled subscription tasks during WebSocket shutdown so their finally blocks run before shared state (DB pools, event loop) is torn down.
Await cancelled subscription tasks during WebSocket shutdown so their finally blocks run before shared state (DB pools, event loop) is torn down.
This release was contributed by @Flamefork in https://github.com/strawberry-graphql/strawberry/pull/4319
Raise MissingFieldAnnotationError instead of MissingReturnAnnotationError when a field using strawberry.field(resolver=...) is missing both a type ann
Raise MissingFieldAnnotationError instead of MissingReturnAnnotationError when a field using strawberry.field(resolver=...) is missing both a type annotation and a resolver return type.
This release was contributed by @youngjaekwon in https://github.com/strawberry-graphql/strawberry/pull/4360
Additional contributors: @pre-commit-ci[bot]
Cancel DataLoader dispatch task when all futures are cancelled.
Cancel DataLoader dispatch task when all futures are cancelled.
Previously, the background task created by dispatch() was fire-and-forget — its
reference was never stored, so it couldn't be cancelled even when no caller was
waiting for the results. This caused wasted work (e.g. database queries against
closed sessions) when using asyncio.TaskGroup or similar structured concurrency
patterns that cancel futures on failure.
The dispatch task is now tracked in Batch._dispatch_task and automatically
cancelled when all futures in the batch are cancelled.
This release was contributed by @ChihebBENCHEIKH1 in https://github.com/strawberry-graphql/strawberry/pull/4379
Fix deprecation_reason not being copied from arguments to auto-generated input type fields in InputMutationExtension.
Fix deprecation_reason not being copied from arguments to auto-generated input type fields in InputMutationExtension.
This release was contributed by @youngjaekwon in https://github.com/strawberry-graphql/strawberry/pull/4353
This release fixes a bug in _listen_to_channel_generator where yield await awaitable inside try/except asyncio.TimeoutError caused the yield to fall w
This release fixes a bug in _listen_to_channel_generator where yield await awaitable inside try/except asyncio.TimeoutError caused the yield to fall
within the try block's bytecode exception table range.
This meant that a TimeoutError thrown into the generator at the yield point
(i.e. when the generator is suspended after delivering a value) was incorrectly
caught by the internal timeout handler, silently stopping the generator instead
of propagating to the caller. In production, this could manifest as
RuntimeError: cannot reuse already awaited coroutine during WebSocket
disconnect cleanup.
The fix splits yield await awaitable into result = await awaitable followed
by yield result, so the yield is outside the try block and exceptions
thrown at the yield point propagate correctly.
This release was contributed by @ben-xo in https://github.com/strawberry-graphql/strawberry/pull/4352
This release attaches error details to Apollo Federation inline tracing (FTV1) trace nodes. This was missing in the original FTV1 addition made in 0.3
This release attaches error details to Apollo Federation inline tracing (FTV1) trace nodes. This was missing in the original FTV1 addition made in 0.314.0.
When a resolver raises an exception, the error message, location, and path are now included in the corresponding trace node, allowing Apollo Studio to display error information alongside timing data.
This release was contributed by @FineAndDanD in https://github.com/strawberry-graphql/strawberry/pull/4351
Additional contributors: @bellini666
This release adds support for Apollo Federation inline tracing (FTV1).
This release adds support for Apollo Federation inline tracing (FTV1).
When a request includes the apollo-federation-include-trace: ftv1 header, Strawberry now records per-resolver timing information and includes it in the response under extensions.ftv1 as a base64-encoded protobuf message, following the Apollo Federation trace format. This allows an Apollo Gateway to aggregate subgraph traces and report them to Apollo Studio.
Install the new optional extra to pull in the required protobuf dependency:
pip install 'strawberry-graphql[apollo-federation]'
Use the async extension for async schemas:
import strawberry
from strawberry.extensions.tracing import ApolloFederationTracingExtension
@strawberry.type
class Query:
@strawberry.field
def hello(self) -> str:
return "Hello, world!"
schema = strawberry.Schema(
query=Query,
extensions=[ApolloFederationTracingExtension],
)
Or the sync version when running outside of an async context:
from strawberry.extensions.tracing import ApolloFederationTracingExtensionSync
schema = strawberry.Schema(
query=Query,
extensions=[ApolloFederationTracingExtensionSync],
)
Security: any client can send the
apollo-federation-include-trace: ftv1header unless you restrict it. Tracing payloads expose resolver timing details, so make sure only a trusted Apollo Gateway (or other internal traffic) can request traces — for example by enforcing authentication, network policy, or stripping the header from public requests at the edge.\
Release contributed by @bellini666 via #4136
Your coding agent can read these notes before it upgrades. Set up the MCP server →