1

I'm using the following versions:

  • "@apollo/gateway": "^2.1.3"
  • "@apollo/server": "^4.0.0"
  • "graphql": "^16.6.0"

I can't get a handle on the req object to extract the headers and forward them. The buildService code works to add headers to requests to downstream services, but the context on ApolloServer is consistently empty. I tried sync and async, request instead of req. I even tried grabbing them directly from context.req.headers, but that's null.

Anyone have any idea on how to accomplish this?

const gateway = new ApolloGateway({
  supergraphSdl: new IntrospectAndCompose({
    subgraphs: [
      { name: "persons", url: process.env.PERSON_SERVER_URL },
    ],
  }),
  buildService({ url }) {
    return new RemoteGraphQLDataSource({
      url,
      willSendRequest: ({ request, context }) => {
        console.log(JSON.stringify(context));
        // TRYING TO INJECT AUTH HEADERS HERE
      }
    });
  }
});

const app = express();
const httpServer = http.createServer(app);

const server = new ApolloServer({
  gateway,
  context: ({ req }) => {
    console.log(JSON.stringify(req));
    // req IS NULL
  },
  plugins: [
    ApolloServerPluginLandingPageDisabled(),
    ApolloServerPluginDrainHttpServer({ httpServer })
  ]
});
await server.start();

const graphqlRoute = "/graphql";
app.use(
  graphqlRoute,
  bodyParser.json(),
  expressMiddleware(server),
);

await new Promise((resolve) => httpServer.listen(process.env.PORT, "0.0.0.0", resolve));
console.log(` Server ready at ${JSON.stringify(httpServer.address())}`);

For what it's worth, I asked here, as well. This feels like it should be a simple flag (especially for federation) to forward the Authorization header.

Dudo
  • 4,002
  • 8
  • 32
  • 57

2 Answers2

1

you need to read headers from request in expressMiddleware, next save them in context and then they will be available in willSendRequest, try:

const gateway = new ApolloGateway({
  supergraphSdl: new IntrospectAndCompose({
    subgraphs: [
      { name: "persons", url: process.env.PERSON_SERVER_URL },
    ],
  }),
  buildService({ url }) {
    return new RemoteGraphQLDataSource({
      url,
      willSendRequest: ({ request, context }) => {
        console.log(JSON.stringify(context));
        for (const [headerKey, headerValue] of Object.entries(context.headers)) {
          request.http?.headers.set(headerKey, headerValue);
        }
      }
    });
  }
});

const app = express();
const httpServer = http.createServer(app);

const server = new ApolloServer({
  gateway,
  plugins: [
    ApolloServerPluginLandingPageDisabled(),
    ApolloServerPluginDrainHttpServer({ httpServer })
  ]
});
await server.start();

const graphqlRoute = "/graphql";

async function context({ req }) {
  return {
    headers: req.headers,
  };
}

app.use(
  graphqlRoute,
  bodyParser.json(),
  expressMiddleware(server, {context: context}),
);

await new Promise((resolve) => httpServer.listen(process.env.PORT, "0.0.0.0", resolve));
console.log(` Server ready at ${JSON.stringify(httpServer.address())}`);
amitlicht
  • 2,868
  • 6
  • 23
  • 27
0

in AS4, ApolloServer has an API that isn't specific to a framework, and the argument to the context function is framework-specific. a la

app.use(
  graphqlRoute,
  cors(),
  bodyParser.json(),
  expressMiddleware(server, {
    context: async ({ req }) => ({
      token: req.headers.authorization
    }),
  }),
);
Dudo
  • 4,002
  • 8
  • 32
  • 57