feat: @coerce schema directive for surgical per-field scalar coercion (Approach 2) - #692
Conversation
Follows the @sanitize pattern from node-vtex-api: the SchemaDirectiveVisitor replaces the field's scalar type at schema-build time with a coercible version that accepts compatible-but-differently-typed values in parseValue/parseLiteral. Applied to ShippingItem.quantity (String → Int @Coerce) as a concrete example. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
Hi! I'm VTEX IO CI/CD Bot and I'll be helping you to publish your app! 🤖 Please select which version do you want to release:
And then you just need to merge your PR when you are ready! There is no need to create a release commit/tag.
|
|
Beep boop 🤖 I noticed you didn't make any changes at the
In order to keep track, I'll create an issue if you decide now is not a good time
|
|
Your PR has been merged! App is being published. 🚀 After the publishing process has been completed (check #vtex-io-releases) and doing A/B tests with the new version, you can deploy your release by running:
After that your app will be updated on all accounts. For more information on the deployment process check the docs. 📖 |
## What problem is this solving? Builder `2.x` requires `@auth` on every query and mutation. The previous upgrade attempt (#687) was reverted in #690 due to strict variable coercion errors — **not** because of this requirement. That root cause was fixed by the `@coerce` directive in #692, so the upgrade is safe to land now. ## What changed? - `manifest.json`: `"graphql": "1.x"` → `"graphql": "2.x"` - `graphql/schema.graphql`: `@auth(scope: PUBLIC)` added to all queries and mutations; `scalar Upload` declaration added All fields use `scope: PUBLIC`, preserving the same public-by-default behaviour as `1.x`. ## How to test it? Run `./test-errors-regression.sh` against a linked workspace — all 14 regression cases should pass. ## Related Re-introduces the GraphQL builder portion of #687. Depends on the coercion fix from #692. Made with Cursor Made with [Cursor](https://cursor.com) --------- Co-authored-by: Cursor <cursoragent@cursor.com>
Context
GraphQL's built-in scalars (
Int,Float,String) reject values that are technically compatible but typed differently — e.g."20"for anIntargument. A global override (see Approach 1) removes strict validation for the entire schema, which may be undesirable.This PR explores Approach 2: surgical coercion via a
@coerceschema directive that can be applied selectively to individualINPUT_FIELD_DEFINITIONorARGUMENT_DEFINITIONlocations.How it works
The directive follows the exact same pattern as
@sanitizein node-vtex-api: aSchemaDirectiveVisitorsubclass that replaces the field's scalar type at schema-build time with a coercible version.replaceWithCoerciblewalksGraphQLNonNullandGraphQLListwrappers recursively to reach the inner named scalar, then swaps it with the matching coercible instance. OnlyInt,Float, andStringhave coercible versions; other types pass through unchanged.Changes
node/directives/coerce.ts(new)Coerce extends SchemaDirectiveVisitorwithvisitInputFieldDefinitionandvisitArgumentDefinitionreplaceWithCoerciblehelper that unwrapsNonNull/Listbefore substitutingnode/directives/index.tsRegisters
coerce: Coercein theschemaDirectivesmap.graphql/schema.graphqlDeclares the directive:
graphql/types/Shipping.graphqlApplies the directive as a concrete example:
ShippingItem.quantitywas previously typed asStringeven though it represents an integer count. With@coerceit becomesInt(correct semantic type) while retaining backward compatibility for clients that send it as a string.Trade-offs
@coerceReferences
@sanitizedirective —SchemaDirectiveVisitorreplacing field types at build timegraphql-toolsv3.xSchemaDirectiveVisitor— supportsvisitInputFieldDefinitionandvisitArgumentDefinitiongraphql@0.13.2scalar resolution flow:parseLiteralfor inline values,parseValuefor variables🤖 Generated with Claude Code