Add custom header to apollo client polling request
Asked Answered
B

3

35

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?

Buote answered 1/2, 2018 at 8:53 Comment(1)
I deleted my answer so that others see this as unanswered.Castilian
A
73

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.

Alagez answered 2/2, 2018 at 8:24 Comment(4)
Ok, but how to setContext if the app has been initialized already and ApolloClient has already been created?Crelin
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 <Query> component?Dodgem
Can you please provide documentation link for the Quick and Easy Solution? I can't get it to work.Spinet
Hi, Just to note on the quick and easy solution - I didnt need the options property - just the context and below worked for meMachicolation
D
12

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 {}
Datha answered 8/8, 2019 at 11:41 Comment(1)
why set cookie in localstorage?Discommodity
S
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,
    },
  };
}
Stator answered 28/2, 2022 at 22:39 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.