nestjs / graphql

GraphQL (TypeScript) module for Nest framework (node.js) 🍷
https://docs.nestjs.com/graphql/quick-start
MIT License
1.45k stars 391 forks source link

Apollo federation with code-first approach adds "@tag" to schema, throwing "invalid definition for directive "@tag" #2646

Closed sebastiangug closed 1 year ago

sebastiangug commented 1 year ago

Is there an existing issue for this?

Current behavior

With the code-first approach and with the federation driver (not the normal one), there is this bit that gets added by default to the schema:

directive @tag(name: String!) repeatable on ARGUMENT_DEFINITION | ENUM | ENUM_VALUE | FIELD_DEFINITION | INPUT_FIELD_DEFINITION | INPUT_OBJECT | INTERFACE | OBJECT | SCALAR | SCHEMA | UNION

This then makes the server throw on startup:

AggregateGraphQLError [GraphQLError]: The schema is not a valid GraphQL schema.. Caused by:
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
.
.
.
.
.
path: undefined,
  locations: undefined,
  extensions: { code: 'GraphQLValidationFailed' },
  causes: [
    GraphQLError: 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

Minimum reproduction code

https://github.com/sebastiangug/nest-graphql-vite

Steps to reproduce

  1. pnpm i
  2. pnpm dev
  3. go to http://localhost:3100/health so the server builds (vite doesn't build it until you make a req)

Expected behavior

Application should start

Package version

10.1.7

Graphql version

graphql: 16.6.0 apollo-server-express: 3.11.1 apollo-server-fastify:

NestJS version

9.3.2

Node.js version

19.5.0

In which operating systems have you tested?

Other

EDIT:

    "@apollo/gateway": "2.1.2",
    "@apollo/subgraph": "2.1.2",

Changing these two dependencies to this version seems to fix the issue, therefore I'm guessing nestjs's code-first approach just doesn't support whatever was changed between those two versions?

Tony133 commented 1 year ago

Hi @sebastiangug,

there is an open issue in the repository of the federation of apollograhql concerning this issue see here: https://github.com/apollographql/federation/issues/2375

so it is not a problem related to the @nestjs/graphql package but rather to the @apollo/subgraph package.

however by updating these packages in your minimal reproduction

"@apollo/gateway": "2.2.3",
"@apollo/subgraph": "2.2.3"

you solve the problem ( see screenshot )

Screenshot 2023-02-12 alle 23 06 45

i hope i have been helpful

geekmini commented 1 year ago

My solution is to use the federation version 2, and you don't have to limit the version of package @apollo/subgraph

    GraphQLModule.forRoot<ApolloFederationDriverConfig>({
      driver: ApolloFederationDriver,
      // use the federation version 2
      autoSchemaFile: isOnline ? false : { path: "schema.gql", federation: 2 },
    })
ddimitrioglo commented 1 year ago

@Tony133 Thank you, man! You've saved my day!

P.S. For those who are exposing the schema as a subgraph "@apollo/subgraph": "2.2.3" is the only library that you need

coler-j commented 1 year ago

Tl;DR:


I think this should be re-opened this error is currently demonstrable in the NestJS sample repo as it currently exists.

This issue is happening in the NestJS sample federated code first example: https://github.com/nestjs/nest/tree/master/sample/31-graphql-federation-code-first

Pull that sample project, install the dependencies of each subgraph with a npm install and then on run you will get:

image

The dependencies of that sample repo are set to:

"@apollo/gateway": "2.4.0",
"@apollo/server": "4.5.0",
"@apollo/subgraph": "2.4.0",
"@nestjs/apollo": "11.0.4",
"@nestjs/graphql": "11.0.0",
"graphql": "16.6.0",

The issue persists if you update all dependencies as well:

"@apollo/gateway": "2.4.6",
"@apollo/server": "4.7.1",
"@apollo/subgraph": "2.4.6",
"@nestjs/apollo": "11.0.6",
"@nestjs/graphql": "11.0.6",

Also a related question:

In the sample codebase, with the above dependencies if you try to use Federation 2 by changing the config to:

GraphQLModule.forRoot<ApolloFederationDriverConfig>({
      driver: ApolloFederationDriver,
      autoSchemaFile: {
        path: "schema.gql", federation: 2
      },
    }),

Then the schema generated by autoSchemaFile will no longer have any federation directives outputted:

# ------------------------------------------------------
# THIS FILE WAS AUTOMATICALLY GENERATED (DO NOT MODIFY)
# ------------------------------------------------------

type User {
  id: ID!
  name: String!
}

type Query {
  getUser(id: ID!): User!
}

But the "@tag" error will go away, but I do not think this is producing a correct subgraph schema. (Seems related to https://github.com/nestjs/graphql/issues/2231 or https://github.com/nestjs/graphql/issues/1597) as directives are missing.

The schema still seems to be usable though through the gateway, and if you introspect through gql playground you can see the directives

image

Introspection of user GQL endpoint running with Federation 2 config:

directive @link(
  url: String
  as: String
  for: link__Purpose
  import: [link__Import]
) on SCHEMA

directive @key(
  fields: federation__FieldSet!
  resolvable: Boolean = true
) on OBJECT | INTERFACE

directive @requires(fields: federation__FieldSet!) on FIELD_DEFINITION

directive @provides(fields: federation__FieldSet!) on FIELD_DEFINITION

directive @external(reason: String) on OBJECT | FIELD_DEFINITION

directive @tag(
  name: String!
) on FIELD_DEFINITION | OBJECT | INTERFACE | UNION | ARGUMENT_DEFINITION | SCALAR | ENUM | ENUM_VALUE | INPUT_OBJECT | INPUT_FIELD_DEFINITION | SCHEMA

directive @extends on OBJECT | INTERFACE

directive @shareable on OBJECT | FIELD_DEFINITION

directive @inaccessible on FIELD_DEFINITION | OBJECT | INTERFACE | UNION | ARGUMENT_DEFINITION | SCALAR | ENUM | ENUM_VALUE | INPUT_OBJECT | INPUT_FIELD_DEFINITION

directive @override(from: String!) on FIELD_DEFINITION

directive @composeDirective(name: String) on SCHEMA

directive @interfaceObject on OBJECT

type User {
  id: ID!
  name: String!
}

type Query {
  getUser(id: ID!): User!
  _entities(representations: [_Any!]!): [_Entity]!
  _service: _Service!
}

enum link__Purpose {
  # `SECURITY` features provide metadata necessary to securely resolve fields.
  SECURITY

  # `EXECUTION` features provide metadata necessary for operation execution.
  EXECUTION
}

scalar link__Import

scalar federation__FieldSet

scalar _Any

type _Service {
  sdl: String
}

union _Entity = User
iamgutz commented 4 months ago

This is happening again on @apollo/subgraph version 2.7.6 Downgrading to version 2.2.3 fixed the problem. Newer version produce this error.