apollographql / apollo-link

:link: Interface for fetching and modifying control flow of GraphQL requests
https://www.apollographql.com/docs/link/
MIT License
1.44k stars 344 forks source link

Does Apollo Link allow passing context from Gateway to another service? #426

Open booboothefool opened 6 years ago

booboothefool commented 6 years ago

This question originates from: https://github.com/apollographql/apollo-server/issues/780 about trying to build an Auth microservice and keeping the current user context from Gateway => Auth.

I read about Apollo Link and think it might be what I need. I tried the following:

Gateway schema

async function makeSchema() {
  const ContextLink = setContext((request, previousContext) => {
    return { foo: 'bar' };  // !!!
  });
  const AuthLink = new HttpLink({
    uri: 'http://localhost:3001/graphql',
    fetch,
  });
  const AuthSchema = makeRemoteExecutableSchema({
    schema: await introspectSchema(AuthLink),
    link: ApolloLink.from([ContextLink, AuthLink]),
  });

  const mergedSchema = mergeSchemas({
    schemas: [AuthSchema],
  });

  return mergedSchema;
}

export const schema = makeSchema();

Auth schema

const typeDefs = `
  type User {
    _id: ID!
    email: String
    jwt: String
  }

  type Query {
    currentUser: User
  }
`;

const resolvers = {
  Query: {
    currentUser: (root, args, ctx) => {
      console.log(ctx.foo);  // IT'S NOT HERE?
    },
  },
};

export const schema = makeExecutableSchema({
  typeDefs,
  resolvers,
});

When I run query currentUser I would expect the context to have been passed from Gateway => Auth to console.log out 'bar', but Auth's ctx.foo is undefined.


EDIT: I realized I can pass it through the header like so 🎉 :

  const ContextLink = setContext((request, previousContext) => ({
    headers: {
      foo: 'bar',
    },
  }));

Can this only be done if I pass it through the header? The following makes me think that's the case:

server.use(
  '/graphql',
  bodyParser.json(),
  graphqlExpress(async request => {
    if (!schema) {
      schema = schemaFunction(process.env);
    }
    const context = await contextFunction(request.headers, process.env);  // !!!
    const rootValue = await rootFunction(request.headers, process.env);

    return {
      schema: await schema,
      rootValue,
      context,
      tracing: true,
    };
  })
);

Where the contextFunction only has access to the request.headers and secrets, but not the full Gateway context.

ziplizard commented 6 years ago

What does your contextFunction look like?

lucas-bremond commented 6 years ago

I am having the same issue... could someone share an example on how to use setContext along with graphqlExpress?

Thank you!