Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Invalid or incomplete schema is created when directives are defined and used. #3775

Closed
TanaseHagi opened this issue Feb 9, 2020 · 5 comments
Labels
🛸 needs-more-information This is at risk of being closed if sufficient details can't be provided.

Comments

@TanaseHagi
Copy link

TanaseHagi commented Feb 9, 2020

GraphQL Playground as well as GraphQL code generator are throwing the following error:

Uncaught Error: Invalid or incomplete schema, unknown type: DirScalarType. Ensure that a full introspection query is used in order to build a client schema.

Code:

import { GraphQLNonNull, GraphQLScalarType, GraphQLInputField, GraphQLInputObjectType, GraphQLArgument, GraphQLInterfaceType, GraphQLField, GraphQLObjectType } from "graphql";
import { Kind } from "graphql";
import { ApolloServer, gql, SchemaDirectiveVisitor } from "apollo-server-express";
import express from "express";

export const app = express();
class MyDirective extends SchemaDirectiveVisitor {
  visitInputFieldDefinition(field: GraphQLInputField, details: {
    objectType: GraphQLInputObjectType;
  }): GraphQLInputField | void | null {
    this.wrapType(field);
  }

  visitArgumentDefinition(argument: GraphQLArgument, details: {
    field: GraphQLField<any, any>;
    objectType: GraphQLObjectType | GraphQLInterfaceType;
  }): GraphQLArgument | void | null {
    this.wrapType(argument);
  }

  wrapType(field: GraphQLInputField | GraphQLArgument) {
    if (
      field.type instanceof GraphQLNonNull &&
      field.type.ofType instanceof GraphQLScalarType
    ) {
      field.type = new GraphQLNonNull(new Type(field.type.ofType, field.name));
      return;
    }

    if (field.type instanceof GraphQLScalarType) {
      field.type = new Type(field.type, field.name);
      return;
    }
  }
}

class Type extends GraphQLScalarType {
  constructor(type: GraphQLScalarType, name: string) {
    console.log("Directive constructor runs!");

    super({
      name: `DirScalarType`,

      // For more information about GraphQLScalar type (de)serialization,
      // see the graphql-js implementation:
      // https://github.com/graphql/graphql-js/blob/31ae8a8e8312/src/type/definition.js#L425-L446

      // to client
      serialize(value) {
        console.log("Directive runs!");
        return value;
      },

      // by client in variable
      parseValue(value) {
        console.log("Directive runs!");
        return value;
      },

      // by client in parameter
      parseLiteral(ast) {
        console.log("Directive runs!");
        switch (ast.kind) {
          case Kind.STRING:
            return ast.value;
          default: {
            return undefined;
          }
        }
      }
    });
  }
}

// Construct a schema, using GraphQL schema language
const typeDefs = gql`
  # scalar DirScalarType
  directive @dir on ARGUMENT_DEFINITION | INPUT_FIELD_DEFINITION
  input MyType {
    name: String @dir
  }
  type Query {
    hello(name: String @dir): String
  }
`;

// Provide resolver functions for your schema fields
const resolvers = {
  Query: {
    hello: () => "Hello world!"
  }
};

const graphQLServer = new ApolloServer({
  typeDefs,
  resolvers,
  schemaDirectives: { dir: MyDirective }
});


graphQLServer.applyMiddleware({ app });

app.listen(4000).on("listening", () => {
  console.log(`listening on server: http://localhost:4000/graphql`);
});

If a scalar type scalar DirScalarType is added in the schema, the error goes away, but the directive fails to run without error.

package.json:

{
  "name": "test",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "start": "ts-node index.ts",
    "test": "echo \"Error: no test specified\" && exit 1",
    "codegen": "graphql-codegen --config codegen.yml --watch"
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "@graphql-codegen/cli": "1.12.2",
    "@graphql-codegen/introspection": "1.12.2",
    "@graphql-codegen/typescript": "1.12.2",
    "@graphql-codegen/typescript-document-nodes": "1.12.2",
    "@graphql-codegen/typescript-resolvers": "1.12.2",
    "apollo-server-express": "2.10.1",
    "express": "4.17.1",
    "graphql": "14.6.0",
    "ts-node": "8.6.2",
    "typescript": "3.7.5"
  }
}

The expected behavior.

run npm run codegen without error.
run graphql playground without error.

The actual behavior.

Running npm run codegen throws an error complaining that the schema is invalid or incomplete.

Running GraphQL Playground will run with error in DevTools Console and query completion will not work in editor.

A simple, runnable reproduction!

https://github.com/TanaseHagi/apollo-server-issue-3775/

@abernix abernix added the 🛸 needs-more-information This is at risk of being closed if sufficient details can't be provided. label Feb 10, 2020
@abernix
Copy link
Member

abernix commented Feb 10, 2020

Thanks for reporting this.

Could you please include answers to the questions that were presented to you in the issue template when you were opening this issue?

### This bug report should include:

- [ ] A short, but descriptive title. The title doesn't need "Apollo" in it.
- [ ] The package name and version of Apollo showing the problem.
- [ ] If applicable, the last version of Apollo where the problem did _not_ occur.
- [ ] The expected behavior.
- [ ] The actual behavior.
- [ ] A **simple**, runnable reproduction!
      Please make a GitHub repository that anyone can clone and run and see the
      problem. Other great ways to demonstrate a reproduction are using our
      CodeSandbox template (https://codesandbox.io/s/apollo-server), or re-mixing
      our Glitch template (https://glitch.com/~apollo-launchpad).

@TanaseHagi TanaseHagi changed the title Apollo Server creates invalid or incomplete schema when directives are defined and used. Invalid or incomplete schema is created when directives are defined and used. Feb 11, 2020
@TanaseHagi
Copy link
Author

@abernix I hope these changes will suffice.

@mfellner
Copy link

mfellner commented May 5, 2020

Related issue here: ardatan/graphql-tools#842
And another one here: ardatan/graphql-tools#789

The consensus appears to be that custom GraphQL scalars should not be used for validation going forward but to use alternative solutions like

What will Apollo's recommended approach to validation be? I think at least the docs at https://www.apollographql.com/docs/graphql-tools/schema-directives/#enforcing-value-restrictions should be updated to reflect the problems with the suggested solution.

@TanaseHagi
Copy link
Author

Thanks @mfellner for the update!

@glasser
Copy link
Member

glasser commented May 6, 2021

Right now we are planning to remove support for schemaDirectives in Apollo Server 3, because the project that implements it (graphql-tools) considers it to be a legacy API. Folks who want to use schemaDirectives or its non-legacy replacement schemaTransforms can use @graphql-tools/schema directly to create a schema passed in with new ApolloServer({schema}); issues relating to these options should probably be opened in the graphql-tools repo.

@glasser glasser closed this as completed May 6, 2021
@github-actions github-actions bot locked as resolved and limited conversation to collaborators Apr 20, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
🛸 needs-more-information This is at risk of being closed if sufficient details can't be provided.
Projects
None yet
Development

No branches or pull requests

4 participants