-
Notifications
You must be signed in to change notification settings - Fork 2.1k
Description
Given this file repro.mjs:
import { buildSchema, validateSchema, parse, validate } from "graphql";
const schema = buildSchema(`
scalar GeoPoint
scalar Float
type Query {
restaurantsNear(loc: GeoPoint!): [String]
}
`)
const schemaErrors = validateSchema(schema);
if (schemaErrors.length > 0) {
console.dir(schemaErrors);
throw new Error("Invalid schema");
}
const doc = parse(`
query ($lat: Float!) {
works: restaurantsNear(loc: [1, 2])
breaks: restaurantsNear(loc: [$lat, 2]) # <---- Validation error here
alsoWorks: restaurantsNear(loc: {_wrapper: [$lat, 2]})
}
`)
const errors = validate(schema, doc);
if (errors.length > 0) {
console.dir(errors);
throw new Error("validate failed");
}Run with npm i graphql && node repro.mjs. (Credits to @benjie for the idea of using a .mjs file for a self-contained repro)
Expected: no errors in all three fields
Actual: The second field named breaks causes a validation error:
[
GraphQLError: Variable "$lat" of type "Float!" used in position expecting type "GeoPoint".
at Object.leave (/path/to/project/node_modules/graphql/validation/rules/VariablesInAllowedPositionRule.js:64:17)
at Object.leave (/path/to/project/node_modules/graphql/language/visitor.js:321:32)
at Object.leave (/path/to/project/node_modules/graphql/utilities/TypeInfo.js:411:21)
at visit (/path/to/project/node_modules/graphql/language/visitor.js:194:21)
at validate (/path/to/project/node_modules/graphql/validation/validate.js:91:24)
at file:///path/to/project/load-standalone.mjs:26:16
at ModuleJob.run (node:internal/modules/esm/module_job:377:25)
at async onImport.tracePromise.__proto__ (node:internal/modules/esm/loader:691:26)
at async asyncRunEntryPointWithESMLoader (node:internal/modules/run_main:101:5) {
path: undefined,
locations: [ [Object], [Object] ],
extensions: [Object: null prototype] {}
}
]
Use case
I want to define a query where part of the scalar is provided by user input and the other half is fixed (using trusted documents). The lat/lng use case is just an example. If it's not convincing enough, consider other scalars such as Range ([low, high], where the user should only be able modify, say, low) or Vector. Or even Raw / JSON.
I looked at the current draft spec (re: VariablesInAllowedPositionRule) and it does not seem to forbid variables within custom scalars. I also looked at the proposed clarification graphql/graphql-spec#1156 and what I'm trying to do isn't against the spirit. Note that while (loc: [$lat, 2]) does not work, (loc: {_wrapper: [$lat, 2]}) works. I don't see adding a dummy object wrapper changes the interpretation of the spec here. So I believe it's just a bug / limitation in graphql-js that
BTW, this also happens in the GraphQL LSP, which I believe share the same validation rules.
(I can work around it by adding an alternative serialization format to my scalar: {_wrapper: [the actual array here]} but it is a hack. Especially, according to GraphQL custom scalar implementation guide, I also need to support it as a serialization format too for compliance. I don't want to commit to supporting this wrapper format as part of my API.)