ApolloServer: Pass Headers From Request Through To Backend

Viewed 3932

I have an ApolloServer that I use to stitch several backend processes together—it literally does nothing else but present a unified front to them all—and I want the headers being passed in the request (which come from React projects) to pass unmolested to the backends (Clojure and Rails).

I have this code:

    let link = new HttpLink({
        uri,
        headers: {"x-api-token": key},
        fetch
    })

And, perhaps not surprisingly, the only thing that the back end sees is "x-api-token".

I have found I can grab the headers from the context:

const server = async (schema = genSchema) => new ApolloServer({
  schema: await schema(),
  context: (ctx) => ({
    headers: ctx.req.headers
  }),
  extensions: [() => new BasicLogging()]
});

And I'm trying to shove those into the outgoing headers somehow, but it seems like a long way around the barn. (And I haven't figured it out yet, too, so I'm not sure I'm on the right track.) Is there no way to tell ApolloServer to just pass through requests?

EDIT: Removed extraneous parenthetical comment. Added code.

Here's an illustration of the problem built using the online examples:

const { ApolloServer, gql } = require('apollo-server');
var fetch = require('node-fetch')
var {introspectSchema, makeRemoteExecutableSchema} = require('graphql-tools')
var {HttpLink} = require('apollo-link-http')

const genSchema = async () => {
  http = new HttpLink({
    uri: "http://localhost:3000/graphql",
    headers: {"x-api-token": "Whatever"},
    fetch
  })
  const link = setContext((request, previousContext) => ({
    headers: {
      'Authentication': "1234xyz"
    }
  })).concat(http);
  const schema = await introspectSchema(link)
  return makeRemoteExecutableSchema({schema, link})
}

const server = async (schema = genSchema) => new ApolloServer({
  schema: await schema(),
});

exports.startServer = async () => {
  const apolloServer = (await server()).listen({port: process.env.PORT || 4000});
  apolloServer.then(({url}) => console.log(`Server ready at ${url}`));
  return apolloServer
}

Put that in server.js and the following in index.js and you can run with "node index.js" (presuming you have a graphql service at locahost:3000):

var server = require('./server')
server.startServer()

What you'll see is that "x-api-token" comes through but "Authentication" does not. The example code uses:

'Authentication': `Bearer ${previousContext.graphqlContext.authKey}`,

...which I could probably use to fetch the headers but literally nothing I put in comes through to the server side.

1 Answers

OK, I found this article:

https://medium.com/wehkamp-techblog/using-headers-with-apollo-datasources-1c4c019d080c

The trick is to set the context when creating the server, which I had figured, but you can't just "pass through" on the headers key. So, server creation could look like this:

const server = async (schema = genSchema) => new ApolloServer({
  schema: await schema(),
  context: ({ req }) => ({
    req,
    customHeaders: {
      headers: {
        ...req.headers,
      },
    },
  }),
});

You do have to shove them into the outgoing message. You could doubtless send them all, but since I'm concerned now about what I might end up overwriting, I just pulled out one:

const http = new HttpLink({
  uri: "http://localhost:3000/graphql",
  fetch
})

const link = setContext((request, previousContext) => ({
  headers: {
    'NewHeader': (previousContext.graphqlContext && previousContext.graphqlContext.customHeaders.headers["myheader"]),
  }
})).concat(http);

The content of the incoming Header "myheader" will come out at the backend as "NewHeader".

Related