Skip to content

Conversation

@EmrysMyrddin
Copy link
Collaborator

@EmrysMyrddin EmrysMyrddin commented Nov 14, 2025

Description

This PR add optional experimental support of the coordinate error attribute proposal (graphql/graphql-spec#1200).

This allows us to more easily identify the resolver which is the source of error than with the path which can be difficult to walk, or even lead to unsolvable ambiguities.

Related to GW-501

Usage

Since this is experimental, it has to be explicitly enabled by adding the appropriate plugin to the
Yoga instance:

import { createYoga, useErrorCoordinate } from 'graphql-yoga'
import { schema } from './schema'

export const yoga = createYoga({
  schema,
  plugins: [useErrorCoordinate()]
})

Once enabled, located errors will gain the coordinate attribute:

const myPlugin = {
  onExecutionResult({ result }) {
    if (result.errors) {
      for (const error of result.errors) {
        console.log('Error at', error.coordinate, ':', error.message)
      }
    }
  }
}

Security concerns

Adding a schema coordinate to errors exposes information about the schema, which can be an attack
vector if you rely on the fact your schema is private and secret.

This is why the coordinate attribute is not serialized by default, and will not be exposed to
clients.

If you want to send this information to client, override either each toJSON error's method, or add
a dedicated extension.

import { GraphQLError } from 'graphql'
import { createYoga, maskError, useErrorCoordinate } from 'graphql-yoga'
import { schema } from './schema'

export const yoga = createYoga({
  schema,
  plugins: [useErrorCoordinate()],
  maskedErrors: {
    isDev: process.env['NODE_ENV'] === 'development', // when `isDev` is true, errors are not masked
    maskError: (error, message, isDev) => {
      if (error instanceof GraphQLError) {
        error.toJSON = () => {
          // Get default graphql serialized error representation
          const json = GraphQLError.prototype.toJSON.apply(error)
          // Manually add the coordinate attribute. You can also use extensions instead.
          json.coordinate = error.coordinate
          return json
        }
      }

      // Keep the default error masking implementation
      return maskError(error, message, isDev)
    }
  }
})

@codesandbox
Copy link

codesandbox bot commented Nov 14, 2025

Review or Edit in CodeSandbox

Open the branch in Web EditorVS CodeInsiders

Open Preview

@EmrysMyrddin EmrysMyrddin changed the title feat(graphql-yoga): add error schema coordinate extension feat(graphql-yoga): add support for experimental error coordinate Nov 14, 2025
@EmrysMyrddin EmrysMyrddin force-pushed the feat/error-schema-coord branch from 89d3ba3 to 947292d Compare November 14, 2025 09:12
@github-actions
Copy link
Contributor

github-actions bot commented Nov 14, 2025

Apollo Federation Subgraph Compatibility Results

Federation 1 Support Federation 2 Support
_service🟢
@key (single)🟢
@key (multi)🟢
@key (composite)🟢
repeatable @key🟢
@requires🟢
@provides🟢
federated tracing🟢
@link🟢
@shareable🟢
@tag🟢
@override🟢
@inaccessible🟢
@composeDirective🟢
@interfaceObject🟢

Learn more:

@github-actions
Copy link
Contributor

github-actions bot commented Nov 14, 2025

💻 Website Preview

The latest changes are available as preview in: https://pr-4288.graphql-yoga.pages.dev

@github-actions
Copy link
Contributor

github-actions bot commented Nov 14, 2025

✅ Benchmark Results

     ✓ no_errors{mode:graphql}
     ✓ expected_result{mode:graphql}
     ✓ no_errors{mode:graphql-jit}
     ✓ expected_result{mode:graphql-jit}
     ✓ no_errors{mode:graphql-response-cache}
     ✓ expected_result{mode:graphql-response-cache}
     ✓ no_errors{mode:graphql-no-parse-validate-cache}
     ✓ expected_result{mode:graphql-no-parse-validate-cache}
     ✓ no_errors{mode:uws}
     ✓ expected_result{mode:uws}

     checks.......................................: 100.00% ✓ 467056      ✗ 0     
     data_received................................: 1.9 GB  13 MB/s
     data_sent....................................: 94 MB   626 kB/s
     http_req_blocked.............................: avg=1.62µs   min=1.01µs   med=1.36µs   max=6.43ms   p(90)=2.08µs   p(95)=2.32µs  
     http_req_connecting..........................: avg=2ns      min=0s       med=0s       max=152.24µs p(90)=0s       p(95)=0s      
     http_req_duration............................: avg=395.9µs  min=217.5µs  med=363.31µs max=20.07ms  p(90)=525.03µs p(95)=559.8µs 
       { expected_response:true }.................: avg=395.9µs  min=217.5µs  med=363.31µs max=20.07ms  p(90)=525.03µs p(95)=559.8µs 
     ✓ { mode:graphql-jit }.......................: avg=296.05µs min=217.5µs  med=278.52µs max=20.07ms  p(90)=310.8µs  p(95)=324.07µs
     ✓ { mode:graphql-no-parse-validate-cache }...: avg=558.64µs min=438.08µs med=530.04µs max=9.39ms   p(90)=600.69µs p(95)=638.7µs 
     ✓ { mode:graphql-response-cache }............: avg=369.48µs min=276.93µs med=350.56µs max=10.9ms   p(90)=386.5µs  p(95)=399.85µs
     ✓ { mode:graphql }...........................: avg=401µs    min=294.53µs med=372.6µs  max=16.09ms  p(90)=429.44µs p(95)=468.27µs
     ✓ { mode:uws }...............................: avg=406.52µs min=300.43µs med=382.85µs max=7.53ms   p(90)=430.53µs p(95)=451.69µs
     http_req_failed..............................: 0.00%   ✓ 0           ✗ 233528
     http_req_receiving...........................: avg=35.33µs  min=16.97µs  med=33.86µs  max=3.25ms   p(90)=41.5µs   p(95)=45.08µs 
     http_req_sending.............................: avg=9.51µs   min=6.13µs   med=8.53µs   max=341.7µs  p(90)=12.44µs  p(95)=13.76µs 
     http_req_tls_handshaking.....................: avg=0s       min=0s       med=0s       max=0s       p(90)=0s       p(95)=0s      
     http_req_waiting.............................: avg=351.04µs min=179.25µs med=319.88µs max=19.9ms   p(90)=479.96µs p(95)=513.42µs
     http_reqs....................................: 233528  1556.836081/s
     iteration_duration...........................: avg=636.9µs  min=405.54µs med=601.11µs max=20.72ms  p(90)=763.26µs p(95)=803.79µs
     iterations...................................: 233528  1556.836081/s
     vus..........................................: 1       min=1         max=1   
     vus_max......................................: 2       min=2         max=2   

@github-actions
Copy link
Contributor

github-actions bot commented Nov 14, 2025

🚀 Snapshot Release (alpha)

The latest changes of this PR are available as alpha on npm (based on the declared changesets):

Package Version Info
@graphql-yoga/apollo-link 5.2.0-alpha-20251120144328-f407246d4f94919701bf3bdd78b9ae60c7059269 npm ↗︎ unpkg ↗︎
@graphql-yoga/urql-exchange 5.2.0-alpha-20251120144328-f407246d4f94919701bf3bdd78b9ae60c7059269 npm ↗︎ unpkg ↗︎
graphql-yoga 5.17.0-alpha-20251120144328-f407246d4f94919701bf3bdd78b9ae60c7059269 npm ↗︎ unpkg ↗︎
@graphql-yoga/nestjs 3.18.0-alpha-20251120144328-f407246d4f94919701bf3bdd78b9ae60c7059269 npm ↗︎ unpkg ↗︎
@graphql-yoga/nestjs-federation 3.18.0-alpha-20251120144328-f407246d4f94919701bf3bdd78b9ae60c7059269 npm ↗︎ unpkg ↗︎
@graphql-yoga/plugin-apollo-inline-trace 3.17.0-alpha-20251120144328-f407246d4f94919701bf3bdd78b9ae60c7059269 npm ↗︎ unpkg ↗︎
@graphql-yoga/apollo-managed-federation 0.16.0-alpha-20251120144328-f407246d4f94919701bf3bdd78b9ae60c7059269 npm ↗︎ unpkg ↗︎
@graphql-yoga/plugin-apollo-usage-report 0.12.0-alpha-20251120144328-f407246d4f94919701bf3bdd78b9ae60c7059269 npm ↗︎ unpkg ↗︎
@graphql-yoga/plugin-apq 3.17.0-alpha-20251120144328-f407246d4f94919701bf3bdd78b9ae60c7059269 npm ↗︎ unpkg ↗︎
@graphql-yoga/plugin-csrf-prevention 3.17.0-alpha-20251120144328-f407246d4f94919701bf3bdd78b9ae60c7059269 npm ↗︎ unpkg ↗︎
@graphql-yoga/plugin-defer-stream 3.17.0-alpha-20251120144328-f407246d4f94919701bf3bdd78b9ae60c7059269 npm ↗︎ unpkg ↗︎
@graphql-yoga/plugin-disable-introspection 2.18.0-alpha-20251120144328-f407246d4f94919701bf3bdd78b9ae60c7059269 npm ↗︎ unpkg ↗︎
@graphql-yoga/plugin-graphql-sse 3.17.0-alpha-20251120144328-f407246d4f94919701bf3bdd78b9ae60c7059269 npm ↗︎ unpkg ↗︎
@graphql-yoga/plugin-jwt 3.11.0-alpha-20251120144328-f407246d4f94919701bf3bdd78b9ae60c7059269 npm ↗︎ unpkg ↗︎
@graphql-yoga/plugin-persisted-operations 3.17.0-alpha-20251120144328-f407246d4f94919701bf3bdd78b9ae60c7059269 npm ↗︎ unpkg ↗︎
@graphql-yoga/plugin-prometheus 6.12.0-alpha-20251120144328-f407246d4f94919701bf3bdd78b9ae60c7059269 npm ↗︎ unpkg ↗︎
@graphql-yoga/plugin-response-cache 3.19.0-alpha-20251120144328-f407246d4f94919701bf3bdd78b9ae60c7059269 npm ↗︎ unpkg ↗︎
@graphql-yoga/plugin-sofa 3.17.0-alpha-20251120144328-f407246d4f94919701bf3bdd78b9ae60c7059269 npm ↗︎ unpkg ↗︎
@graphql-yoga/render-apollo-sandbox 0.2.0-alpha-20251120144328-f407246d4f94919701bf3bdd78b9ae60c7059269 npm ↗︎ unpkg ↗︎
@graphql-yoga/render-graphiql 5.17.0-alpha-20251120144328-f407246d4f94919701bf3bdd78b9ae60c7059269 npm ↗︎ unpkg ↗︎


export const yoga = createYoga({
schema,
plugins: [useErrorCoordinate()]
Copy link
Collaborator

Choose a reason for hiding this comment

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

Should we enable it by default, since error masking will strip it out anyways?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I don't think so, since it is experimental. If the user has it's own masking function, it could leak if it is not updated ^^'

And the OTEL plugin will be able to automatically enable it as needed by using addPlugin (which is also why it's a plugin and not an option!)

Copy link
Collaborator

Choose a reason for hiding this comment

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

😍

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.

3 participants