Skip to content

Commit

Permalink
fix: Remove GlobalID type from relay schema.
Browse files Browse the repository at this point in the history
Having this type in the schema violates the relay spec.
The GlobalID is technically an interpretation of the ID type,
and the schema should always return ID for that.

I created a new scalar type called ID. It's unclear how this
collides with the strawberry.ID, but given that strawberry.ID
isn't actually a scalar type, I am hoping it is ok.
  • Loading branch information
dusty-phillips committed Oct 30, 2023
1 parent 74eb27e commit 63594fe
Show file tree
Hide file tree
Showing 6 changed files with 34 additions and 33 deletions.
13 changes: 8 additions & 5 deletions docs/guides/relay.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,10 +97,9 @@ class Query:
This will generate a schema like this:

```graphql
scalar GlobalID

interface Node {
id: GlobalID!
id: ID!
}

type PageInfo {
Expand All @@ -111,7 +110,7 @@ type PageInfo {
}

type Fruit implements Node {
id: GlobalID!
id: ID!
name: String!
weight: Float!
}
Expand All @@ -127,7 +126,7 @@ type FruitConnection {
}

type Query {
node(id: GlobalID!): Node!
node(id: ID!): Node!
fruits(
before: String = null
after: String = null
Expand Down Expand Up @@ -199,7 +198,7 @@ It can be defined in in the `Query` objects in 4 ways:
a `Node` instance. This is the most basic way to define it.
- `node: Optional[Node]`: The same as `Node`, but if the given object doesn't
exist, it will return `null`.
- `node: List[Node]`: This will define a field that accepts `[GlobalID!]!` and
- `node: List[Node]`: This will define a field that accepts `[ID!]!` and
returns a list of `Node` instances. They can even be from different types.
- `node: List[Optional[Node]]`: The same as `List[Node]`, but the returned list
can contain `null` values if the given objects don't exist.
Expand Down Expand Up @@ -407,6 +406,10 @@ similar use case, like SQLAlchemy, etc.
The `GlobalID` scalar is a special object that contains all the info necessary to
identify and retrieve a given object that implements the `Node` interface.

It constrains the existing `strawberry.ID!` type, which can be any string. Under
relay, the GlobalID is composed of the type and the internal ID, encoded as a base64
string.

It can for example be useful in a mutation, to receive and object and retrieve
it in its resolver. For example:

Expand Down
14 changes: 12 additions & 2 deletions strawberry/relay/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
Iterable,
Iterator,
List,
NewType,
Optional,
Sequence,
Type,
Expand All @@ -26,6 +27,7 @@
)
from typing_extensions import Annotated, Literal, Self, TypeAlias, get_args, get_origin

from strawberry.custom_scalar import scalar
from strawberry.field import field
from strawberry.lazy_type import LazyType
from strawberry.object_type import interface, type
Expand All @@ -41,7 +43,6 @@
from .utils import from_base64, to_base64

if TYPE_CHECKING:
from strawberry.scalars import ID
from strawberry.utils.await_maybe import AwaitableOrValue

_T = TypeVar("_T")
Expand Down Expand Up @@ -298,6 +299,15 @@ def resolve_node_sync(self, info, *, required=False, ensure_type=None) -> Any:

return node

ID = scalar(NewType("ID", GlobalID),
description="ID in the Global ID format, a base64 encoded string that"
"wraps the type and the internal id of the object."
"This is a specialization of the strawberry.ID type, which can be any string.",
specified_by_url="https://graphql.org/learn/global-object-identification/",
serialize=str,
parse_value=GlobalID.from_id
)


class NodeIDPrivate(StrawberryPrivate):
"""Annotate a type attribute as its id.
Expand Down Expand Up @@ -355,7 +365,7 @@ class Node:

@field(name="id", description="The Globally Unique ID of this object")
@classmethod
def _id(cls, root: Node, info: Info) -> GlobalID:
def _id(cls, root: Node, info: Info) -> ID:
# NOTE: root might not be a Node instance when using integrations which
# return an object that is compatible with the type (e.g. the django one).
# In that case, we can retrieve the type itself from info
Expand Down
6 changes: 3 additions & 3 deletions tests/relay/snapshots/schema.gql
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ type CreateFruitPayload {

type Fruit implements Node {
"""The Globally Unique ID of this object"""
id: GlobalID!
id: ID!
name: String!
color: String!
}
Expand All @@ -19,7 +19,7 @@ type FruitAlikeConnection {

type FruitAsync implements Node {
"""The Globally Unique ID of this object"""
id: GlobalID!
id: ID!
name: String!
color: String!
}
Expand Down Expand Up @@ -81,7 +81,7 @@ type Mutation {
"""An object with a Globally Unique ID"""
interface Node {
"""The Globally Unique ID of this object"""
id: GlobalID!
id: ID!
}

"""Information to aid in pagination."""
Expand Down
6 changes: 3 additions & 3 deletions tests/relay/snapshots/schema_future_annotations.gql
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ type CreateFruitPayload {

type Fruit implements Node {
"""The Globally Unique ID of this object"""
id: GlobalID!
id: ID!
name: String!
color: String!
}
Expand All @@ -19,7 +19,7 @@ type FruitAlikeConnection {

type FruitAsync implements Node {
"""The Globally Unique ID of this object"""
id: GlobalID!
id: ID!
name: String!
color: String!
}
Expand Down Expand Up @@ -81,7 +81,7 @@ type Mutation {
"""An object with a Globally Unique ID"""
interface Node {
"""The Globally Unique ID of this object"""
id: GlobalID!
id: ID!
}

"""Information to aid in pagination."""
Expand Down
7 changes: 2 additions & 5 deletions tests/relay/test_fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -1497,7 +1497,7 @@ class Query:
expected = '''
type Fruit implements Node {
"""The Globally Unique ID of this object"""
id: GlobalID!
id: ID!
}
"""A connection to a list of items."""
Expand All @@ -1518,13 +1518,10 @@ class Query:
node: Fruit!
}
"""__GLOBAL_ID_DESC__"""
scalar GlobalID @specifiedBy(url: "https://relay.dev/graphql/objectidentification.htm")
"""An object with a Globally Unique ID"""
interface Node {
"""The Globally Unique ID of this object"""
id: GlobalID!
id: ID!
}
"""Information to aid in pagination."""
Expand Down
21 changes: 6 additions & 15 deletions tests/relay/test_schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ def fruits(self) -> List[Fruit]:
'''
type Fruit implements Node {
"""The Globally Unique ID of this object"""
id: GlobalID!
id: ID!
}
"""A connection to a list of items."""
Expand All @@ -73,13 +73,10 @@ def fruits(self) -> List[Fruit]:
node: Fruit!
}
"""__GLOBAL_ID_DESC__"""
scalar GlobalID @specifiedBy(url: "https://relay.dev/graphql/objectidentification.htm")
"""An object with a Globally Unique ID"""
interface Node {
"""The Globally Unique ID of this object"""
id: GlobalID!
id: ID!
}
"""Information to aid in pagination."""
Expand Down Expand Up @@ -163,7 +160,7 @@ def fruits(self) -> List[Fruit]:
'''
type Fruit implements Node {
"""The Globally Unique ID of this object"""
id: GlobalID!
id: ID!
}
"""A connection to a list of items."""
Expand All @@ -184,13 +181,10 @@ def fruits(self) -> List[Fruit]:
node: Fruit!
}
"""__GLOBAL_ID_DESC__"""
scalar GlobalID @specifiedBy(url: "https://relay.dev/graphql/objectidentification.htm")
"""An object with a Globally Unique ID"""
interface Node {
"""The Globally Unique ID of this object"""
id: GlobalID!
id: ID!
}
"""Information to aid in pagination."""
Expand Down Expand Up @@ -274,7 +268,7 @@ def fruits(self) -> List[Fruit]:
'''
type Fruit implements Node {
"""The Globally Unique ID of this object"""
id: GlobalID!
id: ID!
}
"""A connection to a list of items."""
Expand All @@ -295,13 +289,10 @@ def fruits(self) -> List[Fruit]:
node: Fruit!
}
"""__GLOBAL_ID_DESC__"""
scalar GlobalID @specifiedBy(url: "https://relay.dev/graphql/objectidentification.htm")
"""An object with a Globally Unique ID"""
interface Node {
"""The Globally Unique ID of this object"""
id: GlobalID!
id: ID!
}
"""Information to aid in pagination."""
Expand Down

0 comments on commit 63594fe

Please sign in to comment.