32

I am using the apollo-client library to query data from my Graphql server. Some of the queries are sent to the server every 5 seconds through apollo polling ability.

Is there a generic way to add a custom header to all requests that are sent by my polling client?

gurun8
  • 3,526
  • 12
  • 36
  • 53
Ron Badur
  • 1,873
  • 2
  • 15
  • 34

3 Answers3

72

Two Solutions

There are two ways to do that. One is quick and easy and will work for a specific query with some limitation, and the other is a general solution that is safer and can work for multiple queries.

Quick and Easy Solution

Advantages

  • it's quick
  • and... easy

When you configure your query you can configure it using its options field, that has a context field. The value of context will be processed by the network chain. The context itself is not sent to the server, but if you add a headers field to it, it will be used in the HTTP request.

Example:

const someQuery = graphql(gql`query { ... }`, {
  options: { 
    context: { 
      headers: { 
        "x-custom-header": "pancakes"  // this header will reach the server
      } 
    },
    // ... other options  
  }
})

General Solution using a Network Link middleware

With Apollo you can add an Apollo Link that will act as a middleware and add a custom header to the request based on the context that was set by your query operation.

From the docs:

Apollo Client has a pluggable network interface layer, which can let you configure how queries are sent over HTTP

Read more about Apollo Link, the network link and Middleware concepts.

Advantages:

  • The middleware's logic can be used by any graphql operation (you set the condition)
  • Your queries don't need to "care" or know about HTTP headers
  • You can do more processing before deciding if and what headers to add to the request.
  • and more..

Setting the context

Same as the quick and easy solution, only this time we don't set the headers directly:

 {
   options: { 
     context: { 
       canHazPancakes: true //this will not reach the server
     }
   }
 }

Adding the middleware

Apollo has a specific middleware for setting the context apollo-link-context (the same can be achieved with a more general middleware).

import {setContext} from 'apollo-link-context'

//... 

const pancakesLink = setContext((operation, previousContext) => { 
  const { headers, canHazPancakes } = previousContext
  if (!canHazPancakes) { 
    return previousContext
  }

  return {
    ...previousContext,
    headers: {    
      ...headers,
      "x-with-pancakes": "yes" //your custom header
    }
  }
})

Don't forget to concat it to the network chain somewhere before your http link

const client = new ApolloClient({
  // ...
  link: ApolloLink.from([
    pancakesLink,
    <yourHttpLink>
  ])
})

There is another useful example in the docs: using a middleware for authentication.

That's it! You should get some pancakes from the server now. Hope this helps.

Tal Z
  • 3,170
  • 17
  • 30
  • 7
    Ok, but how to setContext if the app has been initialized already and ApolloClient has already been created? – Otto Jun 20 '19 at 19:39
  • 6
    This is my question as well. The middleware doesn't seem capable of dynamic headers... hopefully I'm wrong? Also how would I go about implementing this within a component? – Nathanael Jul 02 '19 at 13:22
  • Can you please provide documentation link for the Quick and Easy Solution? I can't get it to work. – mikkun Dec 02 '19 at 19:19
  • Hi, Just to note on the quick and easy solution - I didnt need the options property - just the context and below worked for me – alexr89 Mar 28 '21 at 22:58
11

Tal Z's answer is very good. However, I thought I'd just paste how to implement the two methods he's listed for those using Angular.

Adding the header for each individual apollo call

import { Component, OnInit } from '@angular/core';
import { LocalStorageService } from 'angular-2-local-storage';
import { Apollo } from 'apollo-angular';
import gql from 'graphql-tag';
import { Pineapples, Pineapple } from './models/pineapples';

export class AppComponent {

  constructor(private apollo: Apollo,
    private localStorageService: LocalStorageService) {
  }

  callGraphQLQuery() {

    const token = this.localStorageService.get('loginToken');
    this.apollo
      .watchQuery<Pineapples>({

        query: gql`
        {
          pineapples{
            id
            name
          }
        }
      `, 
       context: {
           headers: new HttpHeaders().set("Authorization", "Bearer " + token),
         }
      })
      .valueChanges.subscribe(result => {
        // handle results here
      });


  }

}

Adding the header in the middleware

const uri = 'https://localhost:5001/graphql'; 

export function createApollo(httpLink: HttpLink, localStorage: LocalStorageService) {

  const http = httpLink.create({ uri });

  const authLink = new ApolloLink((operation, forward) => {
    // Get the authentication token from local storage if it exists
    const token = localStorage.get('loginToken');

    // Use the setContext method to set the HTTP headers.
    operation.setContext({
      headers: {
        'Authorization': token ? `Bearer ${token}` : ''
      }
    });

    // Call the next link in the middleware chain.
    return forward(operation);
  });

  return {
    link: authLink.concat(http),
    cache: new InMemoryCache()
  };
}

@NgModule({
  exports: [ApolloModule, HttpLinkModule],
  providers: [
    {
      provide: APOLLO_OPTIONS,
      useFactory: createApollo,
      deps: [HttpLink, LocalStorageService],
    },
  ],
})
export class GraphQLModule {}
Diskdrive
  • 18,107
  • 27
  • 101
  • 167
0

Following Diskdrive's step, I will do same for getServerSideProps of nextjs

export async function getServerSideProps(context) {
  const cookies = context.req.headers.cookie;
  const token = getCookie("tokenId", cookies);

  const { data } = await client2.query({
    query: gql`
      query {
        me {
          firstName
          sureName
        }
      }
    `,
      context: {
        headers: {
          authorization: token,
        },
      },
  });

 
  return {
    props: {
      dataFromServer: data,
    },
  };
}
Chikwado
  • 451
  • 6
  • 7