Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
19 changes: 19 additions & 0 deletions RELEASE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
Release type: minor

Add a new `lexicographic_sort_schema` option to `StrawberryConfig`. When enabled,
the schema's types, fields and arguments are sorted alphabetically, affecting both
the introspection result and the exported SDL. This makes it easier to find
related fields (for example `userById`, `userByName`) in the GraphiQL UI and in
exported `schema.graphql` files.

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

schema = strawberry.Schema(
query=Query,
config=StrawberryConfig(lexicographic_sort_schema=True),
)
```

It defaults to `False`, preserving the existing definition order.
37 changes: 37 additions & 0 deletions docs/types/schema-configurations.md
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,43 @@ schema = strawberry.Schema(
)
```

### lexicographic_sort_schema

By default Strawberry preserves the order in which fields and types are defined.
This can make the introspection UI and the exported `schema.graphql` file harder
to read, since related fields (for example `userById`, `userByName`) are not
grouped together.

Setting `lexicographic_sort_schema` to `True` sorts all types, fields and
arguments alphabetically, affecting both the introspection result and the
exported SDL.

```python
schema = strawberry.Schema(
query=Query, config=StrawberryConfig(lexicographic_sort_schema=True)
)
```

With sorting enabled a schema like:

```graphql
type Query {
userByName(name: String!): User!
allUsers: [User!]!
userById(id: Int!): User!
}
```

becomes:

```graphql
type Query {
allUsers: [User!]!
userById(id: Int!): User!
userByName(name: String!): User!
}
```

Comment on lines +116 to +145

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 Before/after example does not show sorted field order within non-Query types

The opening paragraph mentions that types, fields and arguments are sorted, but the before/after SDL blocks only show the Query type. Readers learning about the option won't see what happens to the associated User type's fields (name, ageage, name). Adding the User type to both the "before" and "after" blocks (matching what the test already asserts) would make the example complete and consistent with the introductory description.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

### info_class

By default Strawberry will create an object of type `strawberry.Info` when the
Expand Down
5 changes: 5 additions & 0 deletions strawberry/schema/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@ class StrawberryConfig:
any type (including NewType) to be used as a GraphQL scalar with
proper type checking support.
batching_config: Configuration for operation batching.
lexicographic_sort_schema: Whether to sort the schema's types, fields and

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.

Maybe just call this sort_schema?

arguments lexicographically. This affects both the introspection
result and the exported SDL, making it easier to find related
fields. Defaults to False, which preserves definition order.
"""

auto_camel_case: InitVar[bool] = None # pyright: reportGeneralTypeIssues=false
Expand All @@ -47,6 +51,7 @@ class StrawberryConfig:
_unsafe_disable_same_type_validation: bool = False
scalar_map: Mapping[object, ScalarDefinition] = field(default_factory=dict)
batching_config: BatchingConfig | None = None
lexicographic_sort_schema: bool = False

def __post_init__(
self,
Expand Down
4 changes: 4 additions & 0 deletions strawberry/schema/schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
GraphQLSchema,
OperationDefinitionNode,
get_introspection_query,
lexicographic_sort_schema,
parse,
validate_schema,
)
Expand Down Expand Up @@ -371,6 +372,9 @@ class Query:

raise

if self.config.lexicographic_sort_schema:
self._schema = lexicographic_sort_schema(self._schema)

# attach our schema to the GraphQL schema instance
self._schema._strawberry_schema = self # type: ignore

Expand Down
82 changes: 82 additions & 0 deletions tests/schema/test_config.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import textwrap

import pytest

import strawberry
from strawberry.schema.config import StrawberryConfig
from strawberry.types.info import Info

Expand Down Expand Up @@ -37,3 +40,82 @@ def test_config_post_init_info_class_is_not_subclass():
StrawberryConfig(info_class=object)

assert str(exc_info.value) == "`info_class` must be a subclass of strawberry.Info"


def test_lexicographic_sort_schema_defaults_to_false():
assert StrawberryConfig().lexicographic_sort_schema is False


def test_lexicographic_sort_schema_preserves_definition_order_by_default():
@strawberry.type
class Query:
@strawberry.field
def zebra(self) -> int: ...

@strawberry.field
def apple(self) -> int: ...

schema = strawberry.Schema(query=Query)

expected = """\
type Query {
zebra: Int!
apple: Int!
}"""

assert str(schema) == textwrap.dedent(expected).strip()


def test_lexicographic_sort_schema_sorts_fields_and_types():
@strawberry.type
class User:
name: str
age: int

@strawberry.type
class Query:
@strawberry.field
def user_by_name(self, name: str) -> User: ...

@strawberry.field
def all_users(self) -> list[User]: ...

Comment on lines 40 to +82

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 Missing introspection ordering test

The RELEASE.md and documentation both state that lexicographic_sort_schema affects "both the introspection result and the exported SDL", but there is no test that verifies the introspection response honours the sorted order. It is possible for the SDL output (via str(schema)) to look sorted while the introspection result served to clients (e.g. GraphiQL) is not, if a future change alters how schema.introspect() iterates the type map. A test calling schema.introspect() and asserting on the ordering of data["__schema"]["types"] or the fields within a type would cover this claim.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

@strawberry.field
def user_by_id(self, id: int) -> User: ...

schema = strawberry.Schema(
query=Query,
config=StrawberryConfig(lexicographic_sort_schema=True),
)

expected = """\
type Query {
allUsers: [User!]!
userById(id: Int!): User!
userByName(name: String!): User!
}

type User {
age: Int!
name: String!
}"""

assert str(schema) == textwrap.dedent(expected).strip()


def test_lexicographic_sort_schema_still_executes():
@strawberry.type
class Query:
@strawberry.field
def hello(self, name: str) -> str:
return f"Hi {name}"

schema = strawberry.Schema(
query=Query,
config=StrawberryConfig(lexicographic_sort_schema=True),
)

result = schema.execute_sync('{ hello(name: "Patrick") }')

assert not result.errors
assert result.data == {"hello": "Hi Patrick"}
Loading