apollographql / federation

🌐  Build and scale a single data graph across multiple services with Apollo's federation gateway.
https://apollographql.com/docs/federation/
Other
661 stars 248 forks source link

A valid schema couldn't be composed. The following composition errors were found in @apollo/gateway #2633

Open Smurfy303 opened 1 year ago

Smurfy303 commented 1 year ago

Issue Description

I am using below latest versions & migrating to @apollo/server v4

"@apollo/gateway": "2.4.8",  
  "@apollo/server": "4.7.4",  
  "graphql": "16.6.0",  
  "type-graphql": "1.1.1",

below is code snippet,

import { ApolloGateway, IntrospectAndCompose, RemoteGraphQLDataSource } from '@apollo/gateway';

class AuthenticatedDataSource extends RemoteGraphQLDataSource {
  willSendRequest({ request, context }) {
    // Pass the user's id from the context to underlying services
    // as a header called `user-id`    
  }
}
const gateway = new ApolloGateway({
  supergraphSdl: new IntrospectAndCompose({
    subgraphs: [
      { name: "userService", url: "http://localhost:4001" }
    ]
  }),
  buildService({ name, url }) {
    return new AuthenticatedDataSource({ url });
  },
});

& my node version is : 18.13.0 & npm 8.19.3

but getting below error,

error:  Error: A valid schema couldn't be composed. The following composition errors were found:
Invalid definition for directive "@tag": "@tag" should have locations FIELD_DEFINITION, OBJECT, INTERFACE, UNION, ARGUMENT_DEFINITION, SCALAR, ENUM, ENUM_VALUE, INPUT_OBJECT, INPUT_FIELD_DEFINITION, but found (non-subset) FIELD_DEFINITION, OBJECT, INTERFACE, UNION, ARGUMENT_DEFINITION, SCALAR, ENUM, ENUM_VALUE, INPUT_OBJECT, INPUT_FIELD_DEFINITION, SCHEMA
  at IntrospectAndCompose.createSupergraphFromSubgraphList (/home/node/app/node_modules/@apollo/gateway/src/supergraphManagers/IntrospectAndCompose/index.ts:118:13)
  at IntrospectAndCompose.updateSupergraphSdl (/home/node/app/node_modules/@apollo/gateway/src/supergraphManagers/IntrospectAndCompose/index.ts:106:32)
  at processTicksAndRejections (node:internal/process/task_queues:95:5)
  at async IntrospectAndCompose.initialize (/home/node/app/node_modules/@apollo/gateway/src/supergraphManagers/IntrospectAndCompose/index.ts:62:30)
  at async ApolloGateway.initializeSupergraphManager (/home/node/app/node_modules/@apollo/gateway/src/index.ts:394:22)
  at async ApolloGateway.load (/home/node/app/node_modules/@apollo/gateway/src/index.ts:304:7)

Link to Reproduction

.

Reproduction Steps

No response

trevor-scheer commented 1 year ago

Is your subgraph running Apollo Server as well? Make sure its version of @apollo/subgraph (or whatever subgraph lib you're using) is also up-to-date. If that doesn't resolve the issue it'll be helpful if you share the subgraph code as well. Ideally, a reproduction that we can run ourselves.

Smurfy303 commented 1 year ago

Thank you for your response @trevor-scheer , In below way I am running ApolloServer,

const { schema, executor } = await gateway.load();
  const server = new ApolloServer({
    schema,
    introspection: true,
    formatError: (formattedError, err: any) => {     
      // tslint:disable-next-line:no-console
      console.error('----- Server error:', err);
      return err;
    },
  });
trevor-scheer commented 1 year ago

You shouldn't be calling gateway.load() anymore. Pass your gateway instance directly to Apollo Server.

See the example here: https://www.apollographql.com/docs/apollo-server/using-federation/apollo-gateway-setup#nodejs-gateway-setup

I also requested information about your subgraph which you didn't provide. Is your subgraph up to date?

Smurfy303 commented 1 year ago

Hello @trevor-scheer , below is code snippet for subgraph

The below subgraph code is called User repo, where I integrated @apollo/server with below versions,

    "@apollo/server": "^4.7.4",
    "@apollo/subgraph": "2.4.8",
   "@hapi/hapi": "21.3.2"
   "graphql": "16.6.0",
   "type-graphql": "1.1.1"

& my node version is : 18.13.0 & npm 8.19.3

 import { ApolloServer } from '@apollo/server';
 import { ApolloServerPluginUsageReportingDisabled } from '@apollo/server/plugin/disabled';
 import { printSchema, buildFederatedSchema as buildApolloFederationSchema } from '@apollo/federation';
 import { buildSchema, BuildSchemaOptions } from 'type-graphql';
 import { GraphQLSchema, specifiedDirectives } from 'graphql';
import { federationDirectives } from '@apollo/subgraph/dist/directives';
import gql from 'graphql-tag';
import { printSchema, buildFederatedSchema as buildApolloFederationSchema } from '@apollo/federation';
import { addResolversToSchema, GraphQLResolverMap } from 'apollo-graphql';
import { buildSchema, BuildSchemaOptions } from 'type-graphql';
import { createResolversMap } from 'type-graphql/dist/utils/createResolversMap';
 const Hapi = require('@hapi/hapi');

 const debugSchemaPath = path.resolve(__dirname, 'schema.gql');

 export async function buildFederatedSchema(
  options: Omit<BuildSchemaOptions, 'skipCheck'>,
  referenceResolvers?: GraphQLResolverMap<any>,
): Promise<GraphQLSchema> {
  // @ts-ignore
  const directives = [...specifiedDirectives, ...federationDirectives, ...(options.directives || [])];
  // @ts-ignore
  const schema = await buildSchema({ ...options, directives, skipCheck: true, validate: false });

  const federatedSchema = buildApolloFederationSchema({
    typeDefs: gql(printSchema(schema)),
    resolvers: createResolversMap(schema) as any,
  });

  if (referenceResolvers) {
    addResolversToSchema(federatedSchema, referenceResolvers);
  }
  return federatedSchema;
}

 // build TypeGraphQL executable schema
  const schema = await buildFederatedSchema({
    // @ts-ignore
    resolvers,
    authChecker : () => {
      return true;
    },
    emitSchemaFile: debugSchemaPath,
    orphanedTypes: [User],
  });

  const server = new ApolloServer({
    schema,
    introspection: true,    
    plugins: [ApolloServerPluginUsageReportingDisabled()],
  });

  const app = Hapi.server({
    host: 4000,
    port: 'localhost',
    debug: { request: ['*'], log: ['*'] },
    compression: false,
  });

  app.route({
    method: 'GET',
    path: '/',
    handler: (request, h) => {
      return h.redirect('graphql');
    },
  });

  try {
    const { url } = await startStandaloneServer(server, {
      context: async ({ req }): Promise<IBackendContext> => {
        const user = await getUserFromHeaders(req);
        const headers = await getCorrelationIdFromHeaders(req);
        return {
          user,
          headers,
        };
      },      
    });
    console.log(`🚀  Server ready at ${url}`);
  }catch(error) {
    console.error('An error occured whilst trying to start the server', error);
  }

but getting error as,

TypeError: Class constructor GraphQLNonNull cannot be invoked without 'new'
next-backend-bos        | next-backend-bos-debug-0  |     at Object.<anonymous> (/home/node/app/node_modules/@apollo/federation/src/directives.ts:29:13)
next-backend-bos        | next-backend-bos-debug-0  |     at Module._compile (internal/modules/cjs/loader.js:1063:30)

I am trying to integrate this subgraph/repo with ApolloGateway

trevor-scheer commented 1 year ago

@apollo/federation has been deprecated for a long time. You need to uninstall that package and import what you need from @apollo/subgraph instead:

import { printSubgraphSchema, buildSubgraphSchema } from '@apollo/subgraph';