Apollo Federation - Custom Directive not working

Hey Everyone,

I am trying to build a library of custom directives to use in different subgraphs created using apollo federation. so for the PoC, I was creating a simple directive and trying to use the same in one of the implementing services. I was getting the below error when trying to start the service:

GraphQLError [Object]: Unknown directive "@auth".
        at Object.Directive (/poc/service-a/node_modules/graphql/validation/rules/KnownDirectivesRule.js:56:29)
        at Object.enter (/poc/service-a/node_modules/graphql/language/visitor.js:323:29)
        at visit (/poc/service-a/node_modules/graphql/language/visitor.js:243:26)
        at Object.validateSDL (/poc/service-a/node_modules/graphql/validation/validate.js:92:22)
        at Object.buildSchemaFromSDL (/poc/service-a/node_modules/apollo-graphql/lib/schema/buildSchemaFromSDL.js:48:31)
        at buildFederatedSchema (/poc/service-a/node_modules/@apollo/federation/dist/service/buildFederatedSchema.js:44:35)
        at Object.<anonymous> (/poc/service-a/index.js:90:14)
        at Module._compile (internal/modules/cjs/loader.js:1201:30)
        at Object.Module._extensions..js (internal/modules/cjs/loader.js:1221:10)
        at Module.load (internal/modules/cjs/loader.js:1050:32) {
      locations: []
    }

while my service code looks like this:

const {
  ApolloServer,
  gql,
  SchemaDirectiveVisitor: sSchemaDirectiveVisitor,
} = require("apollo-server");
const { buildFederatedSchema } = require("@apollo/federation");
const { SchemaDirectiveVisitor } = require("graphql-tools");
// const MyAuthDirective = require("lib-directives");

class MyAuthDirective extends SchemaDirectiveVisitor {
  static getDirectiveDeclaration() {
    return new GraphQLDirective({
      name: "auth",
      locations: [DirectiveLocation.FIELD_DEFINITION, DirectiveLocation.OBJECT],
    });
  }

  visitObject(obj) {
    const token = this.context.userToken;
    if (!token) throw new Error("Unauthorized Error, no token");

    if (token && token.includes("DevUserToken")) {
      return obj;
    } else {
      throw new Error("Unauthorized Access, invalid token");
    }
  }

  visitFieldDefinition(field) {
    const token = this.context.userToken;
    if (!token) throw new Error("Unauthorized Error, no token");

    if (token && token.includes("DevUserToken")) {
      return field;
    } else {
      throw new Error("Unauthorized Access, invalid token");
    }
  }
}

const books = [
  {
    title: "The Awakening",
    author: "Kate Chopin",
  },
  {
    title: "City of Glass",
    author: "Paul Auster",
  },
];
const resolvers = {
  Query: {
    books: () => books,
    authors: () => books.map((i) => i.author),
  },
  // Mutation: {
  //   addBook: (title, author) => {
  //     books.push({ title, author });
  //     return { title, author };
  //   },
  // },
};

const typeDefs = gql`
  type Book @key(fields: "title") {
    title: String
    author: String
  }

  type Author @key(fields: "name") {
    name: String
    books: [Book]
  }

  extend type Query {
    books: [Book] @auth
    authors: [String]
  }

  # extend type Mutation {
  #   addBook(title: String, author: String): Book @auth
  # }

  # type GetBooksAndAuthors {
  #   books: [Book]
  #   authors: [String]
  # }
`;

const directives = {
  auth: MyAuthDirective,
};
console.log(directives);
let schema = buildFederatedSchema([{ typeDefs, resolvers }]);

sSchemaDirectiveVisitor.visitSchemaDirectives(schema, directives);
const server = new ApolloServer({
  schema,
  // schemaDirectives: directives,
  context: ({ req, res }) => {
    const header = req.headers["user-data"];
    console.log("FEDRATED SERVICE: ", header);
    console.log(JSON.parse(Buffer.from(header, "base64").toString()));

    return { userToken: req.headers.authorization };
  },
});

server
  .listen({ port: 4001 })
  .then(({ url }) => console.log(`Service a started at ${url}`));

Please help me identify the issue here as I follow whatever is mentioned in the apollo federation documents.

1 Like

Hey Everyone, never mind. I got the solution. I missed adding the directive in the gql string.

1 Like

Glad you got this sorted and thanks for circling back to close the loop!