@@ -9,6 +9,7 @@ import type { BaseLoaderOptions, Loader, Source } from '@graphql-tools/utils';
99import type { TypedDocumentNode } from '@graphql-typed-document-node/core' ;
1010import { FragmentType , graphql , useFragment as unmaskFragment , useFragment } from '../gql' ;
1111import { SchemaWarningConnection , SeverityLevelType } from '../gql/graphql' ;
12+ import { APIError , IntrospectionError , InvalidFederationSubgraphError } from './errors' ;
1213import { graphqlRequest } from './graphql-request' ;
1314import { Texture } from './texture/texture' ;
1415
@@ -217,66 +218,69 @@ class FederationSubgraphUrlLoader implements Loader {
217218 } ,
218219 } ) ;
219220
220- this . logger ?. debug ?.( 'Attempt "_Service" type lookup via "Query.__type".' ) ;
221-
222- // We can check if the schema is a subgraph by looking for the `_Service` type.
223- const isSubgraph = await client . request ( {
224- operation : parse ( /* GraphQL */ `
225- query ${ 'LookupService' } {
226- __type(name: "_Service") ${ ' ' } {
227- name
228- }
229- }
230- ` ) as TypedDocumentNode < { __type : null | { name : string } } , Record < string , never > > ,
231- } ) ;
232-
233- if ( isSubgraph . __type === null ) {
234- this . logger ?. debug ?.( 'Type not found, this is not a Federation subgraph.' ) ;
235- return [ ] ;
236- }
237-
238- this . logger ?. debug ?.(
239- 'Resolved "_Service" type. Federation subgraph detected.' +
240- 'Attempt Federation introspection via "Query._service" field.' ,
241- ) ;
242-
243- const response = await client . request ( {
244- operation : parse ( /* GraphQL */ `
221+ try {
222+ const response = await client . request ( {
223+ operation : parse ( /* GraphQL */ `
245224 query ${ 'GetFederationSchema' } {
246225 _service {
247226 sdl
248227 }
249228 }
250229 ` ) as TypedDocumentNode < { _service : { sdl : string } } , Record < string , never > > ,
251- } ) ;
230+ } ) ;
252231
253- this . logger ?. debug ?.( 'Resolved subgraph SDL successfully.' ) ;
232+ this . logger ?. debug ?.( 'Resolved subgraph SDL successfully.' ) ;
254233
255- const sdl = minifySchema ( response . _service . sdl ) ;
234+ const sdl = minifySchema ( response . _service . sdl ) ;
256235
257- return [
258- {
259- document : parse ( sdl ) ,
260- rawSDL : sdl ,
261- } ,
262- ] ;
236+ return [
237+ {
238+ document : parse ( sdl ) ,
239+ rawSDL : sdl ,
240+ } ,
241+ ] ;
242+ } catch ( err ) {
243+ if (
244+ err instanceof APIError &&
245+ err . graphQLErrors ?. some (
246+ err =>
247+ err . message . includes ( 'Cannot query field "_service" on type "Query"' ) ||
248+ err . message . includes ( 'Cannot query field "sdl" on type "_Service"' ) ,
249+ )
250+ ) {
251+ throw new InvalidFederationSubgraphError (
252+ 'The GraphQL server responded with the following errors:\n' +
253+ err . graphQLErrors . map ( error => `- ${ error . message } ` ) . join ( '\n' ) ,
254+ ) ;
255+ }
256+ throw err ;
257+ }
263258 }
264259}
265260
266261class FederationSubgraphIntrospectionThenGraphQLIntrospectionUrlLoader implements Loader {
267262 private urlLoader = new UrlLoader ( ) ;
268263 private federationLoader : FederationSubgraphUrlLoader ;
264+
269265 constructor ( private logger ?: LegacyLogger ) {
270266 this . federationLoader = new FederationSubgraphUrlLoader ( logger ) ;
271267 }
272268
273269 async load ( pointer : string , options : BaseLoaderOptions & { headers ?: Record < string , string > } ) {
274- this . logger ?. debug ?.( 'Attempt federation introspection' ) ;
275- let result = await this . federationLoader . load ( pointer , options ) ;
276- if ( ! result . length ) {
277- this . logger ?. debug ?.( 'Attempt GraphQL introspection' ) ;
278- result = await this . urlLoader . load ( pointer , options ) ;
270+ try {
271+ return await this . federationLoader . load ( pointer , options ) ;
272+ } catch ( e ) {
273+ // if this error is because because federated introspection isnt supported, then ignore and try
274+ // normal introspection.
275+ if ( ! ( e instanceof IntrospectionError || e instanceof InvalidFederationSubgraphError ) ) {
276+ // otherwise, raise an introspection error because some unknown error happened during introspection.
277+ // this may be unintuitive, but we don't want to raise an API Error since users may believe our API is the one at fault.
278+ // We'd rather nudge them to look into their service's behavior.
279+ throw new IntrospectionError ( ) ;
280+ }
279281 }
280- return result ;
282+
283+ this . logger ?. debug ?.( 'Query._service not found. This is a not a Federation subgraph.' ) ;
284+ return await this . urlLoader . load ( pointer , options ) ;
281285 }
282286}
0 commit comments