This is my shared module
import { environment } from './../../environments/environment';
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { HttpClientModule, HttpHeaders } from '@angular/common/http';
import { ApolloModule, Apollo } from 'apollo-angular';
import { HttpLinkModule, HttpLink } from 'apollo-angular-link-http';
import { InMemoryCache } from 'apollo-cache-inmemory';
import { ApolloLink } from 'apollo-link';
@NgModule({
imports: [CommonModule, HttpLinkModule],
exports: [
CommonModule,
HttpClientModule,
ApolloModule,
HttpLinkModule
]
})
export class SharedModule {
constructor(apollo: Apollo, httpLink: HttpLink) {
const http = httpLink.create({ uri: environment.graphqlUrl });
const middleware = new ApolloLink((operation, forward) => {
operation.setContext({
headers: new HttpHeaders().set(
'Authorization',
localStorage.getItem('token') || null,
),
});
return forward(operation);
});
const link = middleware.concat(http);
apollo.create({
link: link,
cache: new InMemoryCache()
});
}
}
When I request query
import { Apollo } from 'apollo-angular';
import gql from 'graphql-tag';
@Injectable()
export class MoviesService {
constructor(private apollo: Apollo) {}
}
const QUERY = gql`
query getMovies {
movies(orderBy:{column:"id" order:ASC}) { ... }
}
`;
this.apollo.query({ query: QUERY }).subscribe( ... )
I get this error
core.js:1448 ERROR Error: Uncaught (in promise): Error: Network error: Cannot read property 'length' of null
Error: Network error: Cannot read property 'length' of null
at new ApolloError (ApolloError.js:43)
at eval (QueryManager.js:325)
at eval (QueryManager.js:758)
at Array.forEach (<anonymous>)
at eval (QueryManager.js:757)
at Map.forEach (<anonymous>)
at QueryManager.broadcastQueries (QueryManager.js:752)
at eval (QueryManager.js:252)
at ZoneDelegate.invoke (zone.js:388)
at Object.onInvoke (core.js:4749)
at new ApolloError (ApolloError.js:43)
at eval (QueryManager.js:325)
at eval (QueryManager.js:758)
at Array.forEach (<anonymous>)
at eval (QueryManager.js:757)
at Map.forEach (<anonymous>)
at QueryManager.broadcastQueries (QueryManager.js:752)
at eval (QueryManager.js:252)
at ZoneDelegate.invoke (zone.js:388)
at Object.onInvoke (core.js:4749)
at resolvePromise (zone.js:814)
at eval (zone.js:877)
at ZoneDelegate.invokeTask (zone.js:421)
at Object.onInvokeTask (core.js:4740)
at ZoneDelegate.invokeTask (zone.js:420)
at Zone.runTask (zone.js:188)
at drainMicroTaskQueue (zone.js:595)
at ZoneTask.invokeTask (zone.js:500)
at ZoneTask.invoke (zone.js:485)
at timer (zone.js:2054)
But without middleware (only with http link) it works fine.
Like that
apollo.create({
link: http,
cache: new InMemoryCache()
});
Any suggestions?
Versions
"apollo-angular": "^1.0.1",
"apollo-angular-link-http": "^1.0.3",
"apollo-cache-inmemory": "^1.1.12",
"apollo-client": "^2.2.8",
"apollo-link": "^1.2.2",
"@angular/cli": "~1.7.4",
I'll try to reproduce it and fix it
The same error occurs when using this approach
const auth = setContext((_, { headers }) => {
// get the authentication token from local storage if it exists
const token = localStorage.getItem('token');
// return the headers to the context so httpLink can read them
// in this example we assume headers property exists
// and it is an instance of HttpHeaders
if (!token) {
return {};
} else {
return { headers: headers.append('Authorization', `Bearer ${token}`) };
}
});
Can you provide another way to set headers?
I was able to get around this by checking if a token exists first, and just forwarding the operation without attempting to apply any headers if it doesn't exist:
constructor(apollo: Apollo, httpLink: HttpLink) {
const http = httpLink.create({ uri: environment.graphqlUrl });
const middleware = new ApolloLink((operation, forward) => {
// Check for token
const token = localStorage.getItem('token');
if (!token) return forward(operation);
operation.setContext({
headers: new HttpHeaders().set(
'Authorization',
token,
),
});
return forward(operation);
});
const link = middleware.concat(http);
apollo.create({
link: link,
cache: new InMemoryCache()
});
}
I've implemented Http interceptor and works fine
import { Injectable } from '@angular/core';
import { HttpRequest, HttpHandler } from '@angular/common/http';
@Injectable()
export class TokenInterceptor {
constructor() { }
intercept(request: HttpRequest<any>, next: HttpHandler) {
request = request.clone({
setHeaders: {
Authorization: `Bearer ${localStorage.getItem('token')}`
}
});
return next.handle(request);
}
}
I was trying to implement something similar, aka. update the Apollo object as soon as I log in with Auth header. No success. There's not even a method where I could say this.apollo.getClient().updateHeader or similar.
The approach with http-interceptor was finally a success for me too.
I tried to reproduce it and it works perfectly:
https://stackblitz.com/edit/simple-apollo-angular-example-headers
It would be good to update the docs with this information as the current docs for auth are incorrect.
@herkulano Maybe you could do it? I can help
Most helpful comment
I was able to get around this by checking if a token exists first, and just forwarding the operation without attempting to apply any headers if it doesn't exist: