Skip to content
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/breaking-changes.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ title: List of breaking changes and deprecations

# List of breaking changes and deprecations

- [Version 0.279.0 - 1 August 2025](./breaking-changes/0.279.0.md)
Comment thread
patrick91 marked this conversation as resolved.
Outdated
- [Version 0.268.0 - 10 May 2025](./breaking-changes/0.268.0.md)
- [Version 0.249.0 - 18 November 2024](./breaking-changes/0.249.0.md)
- [Version 0.243.0 - 25 September 2024](./breaking-changes/0.243.0.md)
Expand Down
136 changes: 136 additions & 0 deletions docs/breaking-changes/0.279.0.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
---
title: 0.279.0 Breaking Changes
slug: breaking-changes/0.279.0
---

# v0.279.0 Breaking Changes

This release changes the `strawberry.Maybe` type definition to provide a more
consistent and intuitive API for handling optional fields.

## What Changed

The `Maybe` type definition has been changed from:

```python
Maybe: TypeAlias = Union[Some[Union[T, None]], None]
```

to:

```python
Maybe: TypeAlias = Union[Some[T], None]
```

## Impact on Your Code

### Type Annotations

If you were using `Maybe[T]` and expecting to handle explicit `null` values, you
now need to explicitly declare this with `Maybe[T | None]`:

```python
# Before (0.278.0 and earlier)
field: strawberry.Maybe[str] # Could handle Some(None)

# After (0.279.0+)
field: strawberry.Maybe[str] # Only handles Some("value") or None (absent)
field: strawberry.Maybe[str | None] # Handles Some("value"), Some(None), or None
```

### Runtime Behavior

The runtime behavior changes to provide more consistent field checking:

- `Maybe[str]` now represents "field present with non-null value" or "field
absent"
- `Maybe[str | None]` represents "field present with value", "field present but
null", or "field absent"

This means `Maybe[str]` can no longer receive explicit `null` values - they will
cause a validation error.

### Consistent Field Checking

This change provides a single, consistent way to check if a field was provided,
regardless of whether the field allows null values:

```python
@strawberry.input
class UpdateUserInput:
# Can be provided with a value or not provided at all
name: strawberry.Maybe[str]

# Can be provided with a value, provided as null, or not provided at all
phone: strawberry.Maybe[str | None]


@strawberry.mutation
def update_user(input: UpdateUserInput) -> User:
# Same checking pattern for both fields
if input.name is not None: # Field was provided
user.name = input.name.value # Type checker knows this is str

if input.phone is not None: # Field was provided
user.phone = input.phone.value # Type checker knows this is str | None
```

The key benefit is that `if field is not None` now consistently means "field was
provided" for all Maybe fields.

## Migration

### Automatic Migration

Strawberry provides a codemod to automatically update your code:

```bash
strawberry upgrade maybe-optional
```

The codemod will automatically convert `Maybe[T]` to `Maybe[T | None]` to
maintain the previous behavior.

### Manual Migration

Review your `Maybe` usage and decide whether you need the union type:

```python
# If you only need "present" vs "absent" (most common case)
field: strawberry.Maybe[str]

# If you need "present with value", "present but null", and "absent"
field: strawberry.Maybe[str | None]
```

## Why This Change?

After extensive discussion, we decided that changing `Maybe` to `Some[T] | None`
(instead of `Some[T | None] | None`) provides the best developer experience
because:

1. **Consistent field checking**: You can always use `if field is not None:` to
check if a field was provided, regardless of whether the field allows null
values

2. **Preserves existing behavior**: You can still get the previous behavior by
using `Maybe[T | None]` when you need to handle explicit nulls

3. **Better type safety**: `Maybe[str]` now properly indicates that the field
cannot be null, while `Maybe[str | None]` explicitly allows nulls

4. **Cleaner API**: There's now "one true way" to check if a field was provided,
making the API more intuitive

The new approach provides both simplicity for common cases and flexibility for
complex scenarios where null handling is needed.

## Need Help?

- See the [Maybe documentation](../types/maybe.md) for comprehensive usage
examples
- Check the
[migration guide](../types/maybe.md#migration-from-previous-versions) for
detailed migration instructions
- If you encounter issues, please
[report them on GitHub](https://github.com/strawberry-graphql/strawberry/issues)
52 changes: 14 additions & 38 deletions docs/types/input-types.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,42 +84,10 @@ type Point2D {

</CodeGrid>

When you need to distinguish between a field being set to `null` or its value
and the field being absent, optional arguments would not suffice. In this case
you can use `strawberry.Maybe` like so:

<CodeGrid>

```python
import strawberry
from typing import Optional


@strawberry.input
class UpdateUserInput:
name: str | None = None
phone: strawberry.Maybe[str]


@strawberry.type
class Mutation:
def update_user(self, user_id: strawberry.ID, input: UpdateUserInput) -> User:
if name := input.name:
... # update name...
if input.phone:
phone = input.phone.value # can be str | None
... # update phone...
```

```graphql
type UpdateUserInput {
name: String = null
phone: String

}
```

</CodeGrid>
When you need to distinguish between a field being set to `null` versus being
completely absent (common in update operations), you can use `strawberry.Maybe`.
See the [Maybe documentation](./maybe.md) for comprehensive examples and usage
patterns.

## API

Expand Down Expand Up @@ -162,6 +130,14 @@ input SearchBy @oneOf {

</CodeGrid>

<Note>

OneOf inputs use `strawberry.Maybe` to distinguish between fields that are
explicitly not provided versus those that might be set to null. See the
[Maybe documentation](./maybe.md) for more details on this usage pattern.

</Note>

## Deprecating fields

Fields can be deprecated using the argument `deprecation_reason`.
Expand All @@ -188,15 +164,15 @@ class Point2D:
z: Optional[float] = strawberry.field(
deprecation_reason="3D coordinates are deprecated"
)
label: strawberry.Maybe[str]
label: Optional[str] = None
```

```graphql
input Point2D {
x: Float!
y: Float!
z: Float @deprecated(reason: "3D coordinates are deprecated")
label: String
label: String = null
}
```

Expand Down
Loading
Loading