kamilkisiela / apollo-angular

A fully-featured, production ready caching GraphQL client for Angular and every GraphQL server 🎁
https://apollo-angular.com
MIT License
1.5k stars 309 forks source link

HttpBatchLink seems to expect headers as Map() #1786

Closed julkue closed 1 week ago

julkue commented 2 years ago

Describe the bug

I'm trying to use the HttpBatchLink, just by replacing HttpLink with HttpBachLink, resulting in the following code of my GraphQl module:

import { NgModule } from '@angular/core';
import { ApolloModule, APOLLO_OPTIONS } from 'apollo-angular';
import { HttpBatchLink } from 'apollo-angular/http';
import { InMemoryCache, ApolloLink } from '@apollo/client/core';
import { onError } from '@apollo/client/link/error';
import { setContext } from '@apollo/client/link/context';

import { environment } from '../environments/environment';

const typePolicies = (): any => ({
  // This merge information is necessary for Apollo's InMemoryCache, that seems to struggle with arrays. The
  // CalculatedCart is correctly normalized with ID and __typename, but it's still necessary as of this thread:
  // https://github.com/apollographql/apollo-client/pull/6372#issuecomment-670165700
  // eslint-disable-next-line @typescript-eslint/naming-convention
  CalculatedCart: {
    fields: {
      items: {
        merge: false,
      },
      summaryList: {
        merge: false,
      },
    }
  },
  // eslint-disable-next-line @typescript-eslint/naming-convention
  Query: {
    fields: {
      getCartListing: {
        merge: false,
      }
    }
  }
});

export const createApollo = (httpLink: HttpBatchLink): any => {
  const errorLinkInstance = onError(({graphQLErrors, networkError}) => {
    if (graphQLErrors) {
      graphQLErrors.map(({message, locations, path}) =>
        console.error(
          `[GraphQL error]: Message: ${message}, Location: ${locations}, Path: ${path}`,
        ),
      );
    }
    if (networkError) {
      console.error(`[Network error]: ${JSON.stringify(networkError)}`);
    }
  });
  const authInstance = setContext(() => {
    const basic = environment.api.datahub.authorization.basic;
    if (basic.username && basic.password) {
      return {
        headers: {
          // eslint-disable-next-line @typescript-eslint/naming-convention
          Authorization: `Basic ${btoa(`${basic.username}:${basic.password}`)}`
        },
      };
    }
    return {};
  });
  const httpLinkInstance = httpLink.create({
    uri: `${environment.api.datahub.url}?apikey=${environment.api.datahub.apiKey}`,
    withCredentials: true
  });
  return {
    link: ApolloLink.from([
      errorLinkInstance,
      authInstance,
      httpLinkInstance
    ]),
    cache: new InMemoryCache({typePolicies: typePolicies()})
  };
};

@NgModule({
  imports: [
    ApolloModule,
  ],
  exports: [],
  providers: [
    {
      provide: APOLLO_OPTIONS,
      useFactory: createApollo,
      deps: [HttpBatchLink],
    },
  ],
})
export class GraphQLModule {
}

My expectation would be that this works because this follows the docs. However, instead I'm getting an error in the console throwed by Apollo Angular:

Screenshot 2022-05-24 224628

It seems to complain that headers is not a Map. The error is thrown on this line:

https://github.com/kamilkisiela/apollo-angular/blob/master/packages/apollo-angular/http/src/http-batch-link.ts#L158

So, effectively context.headers.keys() is not working because headers - as you can see in my above example - is an Object, not a Map. However, this follows the documentation as well, therefore I'm confused.

I now even tried to adjust my above authInstance method to the following, making the header a Map:

  const authInstance = setContext(() => {
    const basic = environment.api.datahub.authorization.basic;
    if (basic.username && basic.password) {
      const headers = new Map();
      headers.set('Authorization', `Basic ${btoa(`${basic.username}:${basic.password}`)}`);
      return {
        headers,
      };
    }
    return {};
  });

But that will just lead to the next error:

Screenshot 2022-05-24 225258

Sice I'm following the documentation I'm lost here and I'm assuming that the problem is not in my usage of the library.

Environment:

+-- @angular/cli@13.3.3
+-- @angular/core@13.3.4
+-- @apollo/client@3.5.10
+-- apollo-angular@3.0.1
+-- graphql@16.3.0
`-- typescript@4.6.3
krystadamianpzpn commented 1 year ago
headers: new HttpHeaders({
      "Accept": "*/*, application/json",
      "Content-Type": "application/json",
      "Access-Control-Allow-Headers": "*",
      "Access-Control-Allow-Origin": "*",
      "Access-Control-Allow-Method": "*"
    }),

Thats works for me :)

julkue commented 12 months ago

@krystadamianpzpn Thanks, this also seems to work for me!

Would be great to have it documented.

PowerKiKi commented 1 week ago

https://github.com/kamilkisiela/apollo-angular/issues/1786#issuecomment-1300198082 is correct.

It is documented in https://the-guild.dev/graphql/apollo-angular/docs/data/network#options

image