node.jsgraphqlapollo-servergraphql-toolsgraphql-schema

Trouble migrating from graphql-import to just graphql-tools with ApolloServer, directives cease to work


My plight began as a simple desire to expand my graphql schema from a single .graphql file to multiple files so i can better organize the schema and so it wouldn;t grow to one huge file out of control.

My original layout was very straight forward and i had a working schema in a schema.graphql file. I would be able to parse it into a string using importSchema('server/schema.graphql') from the graphql-import library, which is now deprecated https://github.com/ardatan/graphql-import

They mention that it has been merged into graphql-tools in the newest version and provide a migration tutorial here https://www.graphql-tools.com/docs/migration-from-import The tutorial seems very straight forward since their first example pretty much illustrate exactly what my code looks like (except i dont use es6 import but old fashoined require):

import { importSchema } from 'graphql-import';
import { makeExecutableSchema } from 'graphql-tools';

const typeDefs = importSchema(join(__dirname, 'schema.graphql'));
const resolvers = {
  Query: {...}
};
const schema = makeExecutableSchema({ typeDefs, resolvers });

And then they say to modify it, simply make these changes

import { loadSchemaSync } from '@graphql-tools/load';
import { GraphQLFileLoader } from '@graphql-tools/graphql-file-loader';
import { addResolversToSchema } from '@graphql-tools/schema';

const schema = loadSchemaSync(join(__dirname, 'schema.graphql'), { loaders: [new GraphQLFileLoader()] });
const resolvers = { Query: {...} };

const schemaWithResolvers = addResolversToSchema({
  schema,
  resolvers,
});

I made those changes but the vital difference is that they no longer use makeExecutableSchema() in their example, which is pretty important for me since i need to include the directives. What do i do now with the schema? How do i declare the directives? their documentation for directives still uses makeExecutableSchema but i cant use it anymore since the new loadSchemaSync function returns an object instead of a string literal which i would need to pass to typeDefs in makeExecutableSchema

I am using apollo-server, so it seemed a possible workaround was to just declare the directives in the apollo-server constructor and just pass in this new schemaWithResolvers as a schema as such

const server = new ApolloServer({
    schema, //this includes now the returned value of using addResolversToSchema()
    schemaDirectives : {
        auth:AuthDirective,
        authRole: AuthRoleDirective
    }
    context : ({req}) => //dostuff,

});

This allows my server to run, and i can perform queries and mutations, however, my directives are no longer working, and i no longer have authentication on protected queries.

I would like a way to import my .graphql file and parse it into a string so i can use it inside typeDefs as i used to with importSchema() or a way to declase my directies without using makeExecutableSchema() so that they continue working again!

I have gone up and down the documentation and seen other libraries and so far i keep coming up short, any tips or guidance is greatly appreciated


Solution

  • makeExecutableSchema is still part of graphql-tools and you can continue to use it as shown here in the docs. The issue with the example shown in the docs is that it's not equivalent to what you were doing before. You should use loadTypedefsSync instead:

    import { loadTypedefsSync } from '@graphql-tools/load';
    import { GraphQLFileLoader } from '@graphql-tools/graphql-file-loader';
    import { addResolversToSchema } from '@graphql-tools/schema';
    
    const sources = loadTypedefsSync(join(__dirname, 'schema.graphql'), { loaders: [new GraphQLFileLoader()] });
    const documentNodes = sources.map(source => source.document);
    const resolvers = { Query: {...} };
    
    const schema = makeExecutableSchema({ typeDefs, resolvers });
    

    Alternatively, if you go the loadSchema route, you should be able to apply the directives to your schema after loading it:

    import { SchemaDirectiveVisitor } from "@graphql-tools/utils";
    import { loadSchemaSync } from '@graphql-tools/load';
    import { GraphQLFileLoader } from '@graphql-tools/graphql-file-loader';
    import { addResolversToSchema } from '@graphql-tools/schema';
    
    const schema = loadSchemaSync(join(__dirname, 'schema.graphql'), { loaders: [new GraphQLFileLoader()] });
    const resolvers = { Query: {...} };
    
    const schemaWithResolvers = addResolversToSchema({
      schema,
      resolvers,
    });
    
    SchemaDirectiveVisitor.visitSchemaDirectives(schemaWithResolvers, schemaDirectives);