apollo-server: Bug: It is impossible to implement input type validation directive without breaking Playground

How to reproduce:

Problem:

  • Either directive works, but Playgound doesn’t. (If I follow the instructions then I get this error in the GraphQL Playground console: Invalid or incomplete schema, unknown type: LengthAtMost64.)
  • Or directive does not work, but introspection works. (If I add scalar LengthAtMost64 to my schema then the directive stops working.)

I tried both Apollo Stack v1 and v2.

The GraphQL Playground points to this file: http://cdn.jsdelivr.net/npm/graphql-playground-react@1.7.1/build/static/node_modules/graphql/utilities/buildClientSchema.js Namely, this function:

  function getNamedType(typeName) {
    if (typeDefCache[typeName]) {
      return typeDefCache[typeName];
    }
    var typeIntrospection = typeIntrospectionMap[typeName];
    if (!typeIntrospection) {
      throw new Error('Invalid or incomplete schema, unknown type: ' + typeName + '. Ensure ' + 'that a full introspection query is used in order to build a ' + 'client schema.');
    }
    var typeDef = buildType(typeIntrospection);
    typeDefCache[typeName] = typeDef;
    return typeDef;
  }

I found this bug by trying to use the @constraint directive.

I have no idea what to do.

Are there any other ways to implement input type validation directive?

About this issue

  • Original URL
  • State: closed
  • Created 6 years ago
  • Reactions: 16
  • Comments: 16 (2 by maintainers)

Most upvoted comments

Is there a chance that this is fixable in the foreseeable future?

I was able to dupe and wrote it up under the following: https://github.com/tdharris/apollo-graphql-constraint-directive

I think this is the same behavior seen here. It starts in a working state with the suggested workaround, and then it can be broken quite easily with details provided in the README. I may be off on this, but it seems like the issue here as far as I can tell for now.

This is a problem I have in another project where I am using prisma/graphql-yoga and the directive resolvers have been skipped over as well. My issue with the workaround approach is that I use merge-graphql-schemas since I have layers of schema files and need to have it attempt to merge various duplicates - I have ran into some issues there with apollographql/graphql-tools on that point that I may need to dig around a bit to find again.

The only solution that comes to mind - use Apollo stuff v1. It was a masterpiece. The v2 have a number of flaws and inconveniences yet.

On Sat., 8 Sep. 2018, 03:43 Alexey, notifications@github.com wrote:

I have the same issue. When I use package graphql-constraint-directive and provide scalar and directive to typeDefs, constraint directive stops working and scalar validation is skipped. If I don’t provide scalar and directive to my typeDefs - constraint works fine but IntrospectionSchema query fails on a client and GraphiQL shows errors that scalar is not defined in the schema. In the official apollo-server 2 directive example scalar type also isn’t provided and this example doesn’t work fine on GraphiQL and Playground. Also, a name of the scalar type is dynamically calculated by maxLength. Have you come up with solutions?

[image: image] https://user-images.githubusercontent.com/16045765/45234430-5c8c9100-b2de-11e8-9bf4-31add28ef39f.png

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/apollographql/apollo-server/issues/1303#issuecomment-419514551, or mute the thread https://github.com/notifications/unsubscribe-auth/ABjCLzbGNtMVI0o93UdLL1L-1VH8WBSfks5uYrBMgaJpZM4VE9As .

I have the same issue. When I use package graphql-constraint-directive and provide scalar and directive to typeDefs, constraint directive stops working and scalar validation is skipped. If I don’t provide scalar and directive to my typeDefs - constraint works fine but IntrospectionSchema query fails on a client and GraphiQL shows errors that scalar is not defined in the schema. In the official apollo-server 2 directive example scalar type also isn’t provided and this example doesn’t work fine on GraphiQL and Playground. Also, a name of the scalar type is dynamically calculated by maxLength. Have you come up with solutions?

image

For security reasons the best solution would be to not leak LengthAtMost64 to Playground (client introspection) side. The LengthAtMost64 should stay internal to the server.

That’s why directiveResolvers worked really well in the past. It did not create any types in my schema.

We also got the exact same issue, coming from the documentation: https://www.apollographql.com/docs/graphql-tools/schema-directives/#enforcing-value-restrictions

The example above doesn’t add any scalar XXX to the schema, leading us to believe “this is supposed to work”.

Edit: Found solution in https://github.com/confuser/graphql-constraint-directive/issues/2#issuecomment-487838340.

@evans, It’s not only breaks the Playground. It is also breaks custom scalars

in my case (“apollo-server-express”: “2.0.4”) enabling schemaDirectives leads to such errors:

"GraphQLError: Unknown type "AuthToken". Did you mean "OauthState"?"
1:"    at Object.NamedType (/home/_xxx_/node_modules/graphql/validation/rules/KnownTypeNames.js:66:29)"
2:"    at Object.enter (/home/_xxx_/node_modules/graphql/language/visitor.js:324:29)"
3:"    at Object.enter (/home/_xxx_/node_modules/graphql/language/visitor.js:366:25)"
4:"    at visit (/home/_xxx_/node_modules/graphql/language/visitor.js:254:26)"
5:"    at visitUsingRules (/home/_xxx_/node_modules/graphql/validation/validate.js:74:22)"
6:"    at Object.validate (/home/_xxx_/node_modules/graphql/validation/validate.js:59:10)"
7:"    at Promise.resolve.then (/home/_xxx_/node_modules/apollo-server-core/dist/runQuery.js:92:42)"
8:"    at process._tickCallback (internal/process/next_tick.js:68:7)"

UPD: in my case the problem was in a way I used import: was: import { SchemaDirectiveVisitor } from 'graphql-tools'; must be: import { SchemaDirectiveVisitor } from 'apollo-server-express';

A temporary workaround for Apollo Stack 2.0 was found by @FrankSandqvist in here: https://github.com/confuser/graphql-constraint-directive/issues/2#issuecomment-404399563

TL;DR: make schema from resolvers+directives+typedefs, then merge more scalar typedefs, and then use the final schema in ApolloServer:

    const mainSchema = makeExecutableSchema({
        resolvers,
        typeDefs: myMainTypeDefs,
        schemaDirectives: { constraint: ConstraintDirective }
    });
    const scalarsOnlySchema = makeExecutableSchema({
        typeDefs: readFileSync(path/to/scalars).toString()
    });
    const schema = mergeSchemas({ schemas: [scalarsOnlySchema, mainSchema] });

    const server = new ApolloServer({ schema, introspection: true });

Boom! Now both your directive and Playground are working!

WARNING! The above solution is a bit dirty.

  • You would need to add all your runtime generated directives to the path/to/scalars schema. So you might end up with loads of LengthAtMost64 and StringWithMaxLength50WithMinLength5ThatConformsToEMAIL etc rubbish types in your schema.
  • All those rubbish types are leaking (visible) to your API consumers. Which is what you don’t want in most use cases.

Does this bug affect only DirectiveLocation.INPUT_FIELD_DEFINITION or other directive locations too?