Skip to content
53 changes: 53 additions & 0 deletions RELEASE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
Release type: minor

Remove deprecated `strawberry.scalar(cls, ...)` wrapper pattern and `ScalarWrapper`, deprecated since [0.288.0](https://github.com/strawberry-graphql/strawberry/releases/tag/0.288.0).

You can run `strawberry upgrade replace-scalar-wrappers <path>` to automatically replace built-in scalar wrapper imports.

### Migration guide

**Before (deprecated):**
```python
import strawberry
from datetime import datetime

EpochDateTime = strawberry.scalar(
datetime,
serialize=lambda v: int(v.timestamp()),
parse_value=lambda v: datetime.fromtimestamp(v),
)


@strawberry.type
class Query:
created: EpochDateTime
```

**After:**
```python
import strawberry
from typing import NewType
from datetime import datetime
from strawberry.schema.config import StrawberryConfig

EpochDateTime = NewType("EpochDateTime", datetime)


@strawberry.type
class Query:
created: EpochDateTime


schema = strawberry.Schema(
query=Query,
config=StrawberryConfig(
scalar_map={
EpochDateTime: strawberry.scalar(
name="EpochDateTime",
serialize=lambda v: int(v.timestamp()),
parse_value=lambda v: datetime.fromtimestamp(v),
)
}
),
)
```
54 changes: 34 additions & 20 deletions docs/errors/scalar-already-registered.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,11 @@ the following code will throw this error:

```python
import strawberry
from typing import NewType
from strawberry.schema.config import StrawberryConfig

MyCustomScalar = strawberry.scalar(
str,
name="MyCustomScalar",
)

MyCustomScalar2 = strawberry.scalar(
int,
name="MyCustomScalar",
)
MyCustomScalar = NewType("MyCustomScalar", str)
MyCustomScalar2 = NewType("MyCustomScalar2", int)


@strawberry.type
Expand All @@ -30,7 +25,19 @@ class Query:
scalar_2: MyCustomScalar2


strawberry.Schema(Query)
strawberry.Schema(
Query,
config=StrawberryConfig(
scalar_map={
MyCustomScalar: strawberry.scalar(
name="MyCustomScalar", serialize=str, parse_value=str
),
MyCustomScalar2: strawberry.scalar(
name="MyCustomScalar", serialize=int, parse_value=int
),
}
),
)
```

This happens because different types in Strawberry (and GraphQL) cannot have the
Expand All @@ -48,16 +55,11 @@ name of one of them, for example in this code we renamed the second scalar:

```python
import strawberry
from typing import NewType
from strawberry.schema.config import StrawberryConfig

MyCustomScalar = strawberry.scalar(
str,
name="MyCustomScalar",
)

MyCustomScalar2 = strawberry.scalar(
int,
name="MyCustomScalar2",
)
MyCustomScalar = NewType("MyCustomScalar", str)
MyCustomScalar2 = NewType("MyCustomScalar2", int)


@strawberry.type
Expand All @@ -66,5 +68,17 @@ class Query:
scalar_2: MyCustomScalar2


strawberry.Schema(Query)
strawberry.Schema(
Query,
config=StrawberryConfig(
scalar_map={
MyCustomScalar: strawberry.scalar(
name="MyCustomScalar", serialize=str, parse_value=str
),
MyCustomScalar2: strawberry.scalar(
name="MyCustomScalar2", serialize=int, parse_value=int
),
}
),
)
```
6 changes: 3 additions & 3 deletions docs/general/queries.md
Original file line number Diff line number Diff line change
Expand Up @@ -130,9 +130,9 @@ This can be useful for static typing, as custom scalars are not valid type
annotations.

```python
BigInt = strawberry.scalar(
int, name="BigInt", serialize=lambda v: str(v), parse_value=lambda v: int(v)
)
from typing import NewType

BigInt = NewType("BigInt", int)
Copy link
Member

@patrick91 patrick91 Feb 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we need to define the scalar for this 😊

Copy link
Collaborator Author

@Ckk3 Ckk3 Feb 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, I added it here d541011



@strawberry.type
Expand Down
40 changes: 23 additions & 17 deletions docs/integrations/pydantic.md
Original file line number Diff line number Diff line change
Expand Up @@ -357,7 +357,7 @@ type Query {

Pydantic BaseModels may define a custom type with
[`__get_validators__`](https://pydantic-docs.helpmanual.io/usage/types/#classes-with-__get_validators__)
logic. You will need to add a scalar type and add the mapping to the
logic. You will need to add a scalar definition and add the mapping to the
`scalar_overrides` argument in the Schema class.

```python
Expand All @@ -383,23 +383,24 @@ class Example(BaseModel):
class ExampleGQL: ...


MyScalarType = strawberry.scalar(
MyCustomType,
# or another function describing how to represent MyCustomType in the response
serialize=str,
parse_value=lambda v: MyCustomType(),
)


@strawberry.type
class Query:
@strawberry.field()
def test(self) -> ExampleGQL:
return Example(custom=MyCustomType())


# Tells strawberry to convert MyCustomType into MyScalarType
schema = strawberry.Schema(query=Query, scalar_overrides={MyCustomType: MyScalarType})
schema = strawberry.Schema(
query=Query,
scalar_overrides={
MyCustomType: strawberry.scalar(
name="MyScalarType",
# or another function describing how to represent MyCustomType in the response
serialize=str,
parse_value=lambda v: MyCustomType(),
)
},
)
```

## Custom Conversion Logic
Expand Down Expand Up @@ -429,11 +430,7 @@ class User(BaseModel):
hash: bytes


Base64 = strawberry.scalar(
NewType("Base64", bytes),
serialize=lambda v: base64.b64encode(v).decode("utf-8"),
parse_value=lambda v: base64.b64decode(v.encode("utf-8")),
)
Base64 = NewType("Base64", bytes)


@strawberry.experimental.pydantic.type(model=User)
Expand All @@ -449,7 +446,16 @@ class Query:
return UserType.from_pydantic(User(id=123, hash=b"abcd"))


schema = strawberry.Schema(query=Query)
schema = strawberry.Schema(
query=Query,
scalar_overrides={
Base64: strawberry.scalar(
name="Base64",
serialize=lambda v: base64.b64encode(v).decode("utf-8"),
parse_value=lambda v: base64.b64decode(v.encode("utf-8")),
)
},
)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should this be in scalar map instead?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done! d541011


print(schema.execute_sync("query { test { id, hash } }").data)
# {"test": {"id": "123", "hash": "YWJjZA=="}}
Expand Down
4 changes: 2 additions & 2 deletions docs/types/schema.md
Original file line number Diff line number Diff line change
Expand Up @@ -108,9 +108,9 @@ schema = strawberry.Schema(Query, types=[Individual, Company])

List of [extensions](/docs/extensions) to add to your Schema.

#### `scalar_overrides: Optional[Dict[object, ScalarWrapper]] = None`
#### `scalar_overrides: Optional[Dict[object, ScalarDefinition]] = None`

Override the implementation of the built in scalars.
Override the implementation of the built-in scalars.
[More information](/docs/types/scalars#overriding-built-in-scalars).

---
Expand Down
22 changes: 13 additions & 9 deletions strawberry/codegen/query_codegen.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@
)
from strawberry.types.enum import StrawberryEnumDefinition
from strawberry.types.lazy_type import LazyType
from strawberry.types.scalar import ScalarDefinition, ScalarWrapper
from strawberry.types.scalar import ScalarDefinition
from strawberry.types.union import StrawberryUnion
from strawberry.types.unset import UNSET
from strawberry.utils.str_converters import capitalize_first, to_camel_case
Expand Down Expand Up @@ -541,14 +541,18 @@ def _get_field_type(
not isinstance(field_type, StrawberryType)
and field_type in self.schema.schema_converter.scalar_registry
):
field_type = self.schema.schema_converter.scalar_registry[field_type] # type: ignore

if isinstance(field_type, ScalarWrapper):
python_type = field_type.wrap
if hasattr(python_type, "__supertype__"):
python_type = python_type.__supertype__

return self._collect_scalar(field_type._scalar_definition, python_type) # type: ignore
# Store the original Python type (could be a type or NewType)
# before replacing with the ScalarDefinition
original_python_type = field_type
# For NewTypes, get the underlying type for the codegen
if hasattr(original_python_type, "__supertype__"):
python_type = original_python_type.__supertype__
elif isinstance(original_python_type, type):
python_type = original_python_type
else:
python_type = None
field_type = self.schema.schema_converter.scalar_registry[field_type]
return self._collect_scalar(field_type, python_type)

if isinstance(field_type, ScalarDefinition):
return self._collect_scalar(field_type, None)
Expand Down
5 changes: 1 addition & 4 deletions strawberry/exceptions/invalid_union_type.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ def __init__(
union_definition: StrawberryUnion | None = None,
) -> None:
from strawberry.types.base import StrawberryList
from strawberry.types.scalar import ScalarWrapper

self.union_name = union_name
self.invalid_type = invalid_type
Expand All @@ -37,9 +36,7 @@ def __init__(
# one is our code checking for invalid types, the other is the caller
self.frame = getframeinfo(stack()[2][0])

if isinstance(invalid_type, ScalarWrapper):
type_name = invalid_type.wrap.__name__
elif isinstance(invalid_type, StrawberryList):
if isinstance(invalid_type, StrawberryList):
type_name = "list[...]"
else:
try:
Expand Down
Loading
Loading