Skip to content

Override the types of input object custom scalar fields - #148

Merged
saga-dasgupta merged 2 commits into
mainfrom
input-custom-scalar-overrides
Oct 6, 2026
Merged

saga-dasgupta merged 2 commits into
mainfrom
input-custom-scalar-overrides

Conversation

@saga-dasgupta

Copy link
Copy Markdown
Contributor

query takes custom_scalar_overrides to give a custom scalar in a query's output a specific Rust type. Input objects have no equivalent, so a field typed with a catch-all scalar such as JSON can only be the scalar's type, usually an untyped value.

This adds the same option to typegen, for custom scalar fields of input objects:

#[typegen([
    scalar MyScalar
    input MyInput { myScalar: MyScalar, myScalarList: [MyScalar!]! }
    ...
], custom_scalar_overrides = {
    "MyInput.myScalar" => ::std::primitive::i32,
    "MyInput.myScalarList" => ::std::primitive::i32,
})]
  • Paths are "InputObject.field", and the field's base type must be a custom scalar. Lists and nullability are kept, so myScalar above becomes Option<i32> and myScalarList becomes Vec<i32>. oneOf input objects are covered too, for their variants.
  • Types not starting with :: are relative to the schema module, as with query.
  • Borrowing: an override that borrows gives the input object an 'a lifetime, and needs borrow = true, as with query.
  • Validation: it's a typegen error when a path isn't a custom scalar field of an input object, or when an override borrows without borrow = true.
  • Shared code: CustomScalarOverride gets parse_all and validate_all, which both options now use. The query option's behaviour and error messages don't change.

Compatibility

Nothing that compiles on main changes: the option is new, and without it the generated types are the same.

`typegen` now takes `custom_scalar_overrides`, like `query` does, to
override the type of a custom scalar field of an input object. Paths are
`"InputObject.field"`, and lists and nullability are kept. This lets a
field typed with a catch-all scalar such as `JSON` use a specific Rust
type instead of an untyped value.

Parsing and validation are shared with the `query` option, whose
behaviour doesn't change.
@saga-dasgupta
saga-dasgupta marked this pull request as ready for review October 6, 2026 14:31
saga-dasgupta added a commit to Shopify/shopify-function-rust that referenced this pull request Oct 6, 2026
A prepare target returns the variables for its run target's input query as
untyped `JSON`. Nothing checked that they matched what the run query declares,
so a renamed, added, or retyped variable deployed fine and failed at checkout,
with the run query resolving nothing.

bluejay now generates a struct for the variables of each operation that
declares them: `<Operation>Variables`, or `RootVariables` for an anonymous
operation, with a field per variable. A non-null variable with no default is a
plain field; any other is an `Option` whose `None` omits it. `typegen`
implements wasm_api's `Serialize` and `Deserialize` for it with the same code
as input objects, so it serializes directly.

bluejay's `typegen` also takes `custom_scalar_overrides`, so a prepare result's
`variables` field can be typed as the run query's variables struct instead of
`JsonValue`:

    #[typegen("schema.graphql", custom_scalar_overrides = {
        "CartValidationsGeneratePrepareResult.variables" => cart_validations_generate_run::InputVariables,
    })]

A prepare target that no longer matches its run query then fails to compile:

- a renamed or removed variable is an unknown field (E0560)
- an added variable is a missing field (E0063)
- a retyped variable, or a hand-written `JsonValue`, is a mismatch (E0308)

Depends on Shopify/bluejay#149 and Shopify/bluejay#148, patched in from
Shopify/bluejay#149's branch, which has both, until they ship in a release.

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.

maybe we should move CustomScalarOverride to input.rs now that it isn't specific to executables now?

@saga-dasgupta saga-dasgupta Oct 6, 2026 •

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Yep, moved to input.rs in f28d633

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.

can we use schema instead of input in the path? and also update bluejay-typegen/tests/validation_cases/error/invalid_custom_scalar_overrides.rs to be invalid_query_custom_scalar_overrides.rs?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done in f28d633 this is now invalid_schema_custom_scalar_overrides.rs, and the existing case is invalid_query_custom_scalar_overrides.rs.

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.

same comment as above about the file name conventions

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done, now we have valid_schema_custom_scalar_override.rs and valid_query_custom_scalar_override.rs.

It is no longer specific to executable definitions. The test cases for
the `query` and `typegen` options are now named `*_query_*` and
`*_schema_*`.
@saga-dasgupta
saga-dasgupta merged commit 12a4a0d into main Oct 6, 2026
5 checks passed
@saga-dasgupta
saga-dasgupta deleted the input-custom-scalar-overrides branch October 6, 2026 17:41
@saga-dasgupta saga-dasgupta mentioned this pull request Oct 7, 2026
saga-dasgupta added a commit to Shopify/shopify-function-rust that referenced this pull request Oct 8, 2026
0.5.0 is published with Shopify/bluejay#148 and #149, so the macro depends on it and the workspace
no longer patches the bluejay crates in from #149's branch, which is deleted now that it's merged.
makrisoft pushed a commit to Shopify/shopify-function-rust that referenced this pull request Oct 13, 2026
* Type prepare results with the run query's variables struct

A prepare target returns the variables for its run target's input query as
untyped `JSON`. Nothing checked that they matched what the run query declares,
so a renamed, added, or retyped variable deployed fine and failed at checkout,
with the run query resolving nothing.

bluejay now generates a struct for the variables of each operation that
declares them: `<Operation>Variables`, or `RootVariables` for an anonymous
operation, with a field per variable. A non-null variable with no default is a
plain field; any other is an `Option` whose `None` omits it. `typegen`
implements wasm_api's `Serialize` and `Deserialize` for it with the same code
as input objects, so it serializes directly.

bluejay's `typegen` also takes `custom_scalar_overrides`, so a prepare result's
`variables` field can be typed as the run query's variables struct instead of
`JsonValue`:

    #[typegen("schema.graphql", custom_scalar_overrides = {
        "CartValidationsGeneratePrepareResult.variables" => cart_validations_generate_run::InputVariables,
    })]

A prepare target that no longer matches its run query then fails to compile:

- a renamed or removed variable is an unknown field (E0560)
- an added variable is a missing field (E0063)
- a retyped variable, or a hand-written `JsonValue`, is a mismatch (E0308)

Depends on Shopify/bluejay#149 and Shopify/bluejay#148, patched in from
Shopify/bluejay#149's branch, which has both, until they ship in a release.

* Update bluejay to Shopify/bluejay#149's rebased branch

* Use bluejay 0.5.0

0.5.0 is published with Shopify/bluejay#148 and #149, so the macro depends on it and the workspace
no longer patches the bluejay crates in from #149's branch, which is deleted now that it's merged.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants