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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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
Copy Markdown
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