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

3

6

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

Apul answered 18/6, 2020 at 2:33 Comment(0)
R
4

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);
Ritualist answered 18/6, 2020 at 10:57 Comment(9)
i cannot find loadTypedefsSync in the documentation anywhere, so not sure how you know about it but thanks! this solution worked for me!Apul
So both solutions worked for me, so thanks, however, the first solution using loadTypedefsSync did not allow me to import multiple .graphql files and use the #import feature within the files, but the second approach did. Do you have an approach for using your first solution but still being able to import multiple .graphql files and use the #import feature within the files?Apul
loadSchema uses loadTypedefs under the hood, so I would expect the behavior to be same with regard to the import syntax. You may want to open an issue against the repo with that questionRitualist
how do you find out about these functions? loadTypedefs i cannot find in the documentation for graphql-tools and the repo for graphql-tools/load gave me a 404, do you straight up look at the source code?Apul
If anyone tripped up trying solution #1 going from documentNodes to typeDefs, you need to import { concatAST } from "graphql" and then typeDefs = concatAST(documentNodes)Gabfest
So far I haven't used makeExcecutableSchema since it seems I could avoid it. Here's my tweak to get Apollo running: typeDefs = sources.map(source => source.document); new ApolloServer({ typeDefs, resolvers, ... })Thomasthomasa
documentNodes in the first solution should be typeDefsIngenuous
When using the second approach, how do I create schemaDirectives var from a DocumentNode object containing the directive declarations?Premature
In the first example you aren't even including the schema directives.Seism
P
2

I tried this way but I couldn't solve the problem. A unique solution that managed to take the following approach:

const { ApolloServer, makeExecutableSchema, gql} = require('apollo-server-express')
const { loadTypedefsSync }  = require('@graphql-tools/load')
const { GraphQLFileLoader } = require('@graphql-tools/graphql-file-loader')
const path = require('path')

const sources = loadTypedefsSync(
    path.resolve(__dirname, '../schema/root.graphql'),
    { loaders: [new GraphQLFileLoader()] }
)
const typeDefs = sources.map(source => source.document)
const schema = makeExecutableSchema({
    typeDefs: gql`${typeDefs[0]}`,
    resolvers,
})
Problematic answered 5/7, 2020 at 23:23 Comment(0)
E
0

I had the same issue I loaded the Schema via .graphql and I want to add the graphql-constraint-directive. My Solution was to load the schema with loadSchemaSync and then to use the wrapSchema to use the transform functions, you need also to add the directives into one of your .graphql files:

import { addResolversToSchema, wrapSchema } from 'graphql-tools';
import { GraphQLSchema } from 'graphql';
import resolvers from './resolver';

schema = loadSchemaSync('./**/*.graphql', {
  loaders: [new GraphQLFileLoader()],
});

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

const { constraintDirective } = require('graphql-constraint-directive')
  
const schemaConstrain = wrapSchema({
  schema: schemaWithResolver,
  transforms: [constraintDirective()]
})

Documentation to Schema Wrapping

Exigible answered 28/4, 2021 at 12:52 Comment(1)
You don't need to use wrapSchema. It is not ideal. It adds an additional delegation step which might have performance implications please follow graphql-constraint-directive docs for this. We don't use wrapSchema in any of examples about directives in GraphQL Tools documentation.Hayne

© 2022 - 2024 — McMap. All rights reserved.