Intended outcome:
This TypeScript error occurs with a link chain using additive composition and @apollo/client.
The goal is to create an Apollo Link composed of an Error Link, a Context Link and an HttpLink or HttpBatchLink.
import { ApolloClient, ApolloLink, InMemoryCache, HttpLink } from '@apollo/client';
import { BatchHttpLink } from '@apollo/link-batch-http';
import { onError } from '@apollo/link-error';
import AuthLink from './authLink'
let httpLink: HttpLink | BatchHttpLink;
if (enableBatching === true) {
httpLink = new BatchHttpLink({
...authenticatedHttpSettings,
batchMax: 30,
});
} else {
httpLink = new HttpLink(authenticatedHttpSettings);
}
const errorLink = onError(({ graphQLErrors, operation }) => {
if (graphQLErrors) {...}
});
const link = from([errorLink, AuthLink(), httpLink]);
const testApi = new ApolloClient({
connectToDevTools: true,
link,
cache: new InMemoryCache(),
})
Actual outcome:
TypeScript throws an error:
Type 'ApolloLink' is not assignable to type 'ApolloLink | RequestHandler'.
How to reproduce the issue:
I assume the issue is related to different type definitions for Apollo Link. This should be reproducible with the given code in a Typescript / React environment.
How to solve this issue
Currently @apollo/link-http-batch v.2.0.0-beta.3
is using @apollo/client v.3.0.0-beta.23
. Downgrading my @apollo/client
to 3.0.0-beta.23 worked, but to solve this issue the @apollo/client version should be upgraded in all link packages.
Versions
"react": "^16.12.0",
"react-dom": "^16.12.0",
"typescript": "^3.7.3",
"@apollo/client": "^3.0.0-beta.38",
"@apollo/link-batch-http": "^2.0.0-beta.3",
"@apollo/link-context": "^2.0.0-beta.3",
"@apollo/link-error": "^2.0.0-beta.3",
"apollo": "^2.24.0",
Full Stack Trace
Type 'import("/path/node_modules/@apollo/link-batch-http/node_modules/@apollo/client/link/core/ApolloLink").ApolloLink' is not assignable to type 'import("/path/node_modules/@apollo/client/link/core/ApolloLink").ApolloLink'.
Types of property 'split' are incompatible.
Type '(test: (op: import("/path/node_modules/@apollo/link-batch-http/node_modules/@apollo/client/link/core/types").Operation) => boolean, left: import("/path/node_modules/@apollo/link-batch-http/node_modules/@apollo/client/...' is not assignable to type '(test: (op: import("/path/node_modules/@apollo/client/link/core/types").Operation) => boolean, left: import("/path/node_modules/@apollo/client/link/core/ApolloLink").ApolloLink | import("/path...'.
Types of parameters 'left' and 'left' are incompatible.
Type 'import("/path/node_modules/@apollo/client/link/core/ApolloLink").ApolloLink | import("/path/node_modules/@apollo/client/link/core/types").RequestHandler' is not assignable to type 'import("/path/node_modules/@apollo/link-batch-http/node_modules/@apollo/client/link/core/ApolloLink").ApolloLink | import("/path/node_modules/@apollo/link-batch-http/node_modules/@apollo/client/link/core/types").Reque...'.
Type 'ApolloLink' is not assignable to type 'ApolloLink | RequestHandler'.
Type 'import("/path/node_modules/@apollo/client/link/core/ApolloLink").ApolloLink' is not assignable to type 'import("/path/node_modules/@apollo/link-batch-http/node_modules/@apollo/client/link/core/ApolloLink").ApolloLink'.
Types of property 'split' are incompatible.
Type '(test: (op: import("/path/node_modules/@apollo/client/link/core/types").Operation) => boolean, left: import("/path/node_modules/@apollo/client/link/core/ApolloLink").ApolloLink | import("/path...' is not assignable to type '(test: (op: import("/path/node_modules/@apollo/link-batch-http/node_modules/@apollo/client/link/core/types").Operation) => boolean, left: import("/path/node_modules/@apollo/link-batch-http/node_modules/@apollo/client/...'.
Types of parameters 'left' and 'left' are incompatible.
Type 'import("/path/node_modules/@apollo/link-batch-http/node_modules/@apollo/client/link/core/ApolloLink").ApolloLink | import("/path/node_modules/@apollo/link-batch-http/node_modules/@apollo/client/link/core/types").Reque...' is not assignable to type 'import("/path/node_modules/@apollo/client/link/core/ApolloLink").ApolloLink | import("/path/node_modules/@apollo/client/link/core/types").RequestHandler'.
Type 'ApolloLink' is not assignable to type 'ApolloLink | RequestHandler'.
same issue, code is identical to docs: https://www.apollographql.com/docs/react/v3.0-beta/networking/authentication/ > header
and am importing useContext
from @apollo/link-context
Also seeing this updating from beta.36 to beta.39. Also using useContext
from @apollo/[email protected]
"apollo-link": "^1.2.3",
"apollo-link-context": "^1.0.19",
"apollo-link-http": "^1.5.16",
I am running into the same error while using setContext. I am using the above mentioned package version. Any thoughts how I can resolve this error?
also saw this error upgrading @apollo/client
from beta.38
-> beta.39
when using setContext
from @apollo/[email protected]
The same issue here...
I'm not using the errorLink at all any ideas?
The warning is gone after upgrading to @apollo/[email protected]
.
I still have this error, so I used this approach:
link: from([(errorLink as unknown) as ApolloLink, httpLink]),
This issue seems to be resolved in beta.43, but present from beta.44 onward.
@austin43 Thanks for the info, we'll hold on upgrading until this regression is fixed.
@vicary to get around the blocker temporarily, @wamujlb's solution is certainly viable. It is just a types issue so shouldn't affect anything during runtime.
For me upgrading client and link-error fixed the problem:
"@apollo/client": "^3.0.0-beta.49",
"@apollo/link-error": "^2.0.0-beta.3"
@GiselaMD Not sure if I've been missing something, tried beta.49 and beta.50 and they both gave me type errors on all @apollo/link-error
, @apollo/link-context
and @apollo/link-retry
.
"devDependencies": {
"@apollo/client": "3.0.0-beta.49",
"@apollo/link-context": "^2.0.0-beta.3",
"@apollo/link-error": "^2.0.0-beta.3",
"@apollo/link-retry": "^2.0.0-beta.3",
@vicary try fixing @apollo/client version inside @apollo/link-error and other apollo dependencies, they must be the same version inside those packages. In my case I use yarn, so I set yarn resolution in my package like this, for example:
"resolutions": {
"@apollo/client": "3.0.0-beta.43",
}
@GiselaMD So are we forcing the deps of @apollo/client
inside @apollo/link-*
to match the top-level, such that a dependencies
of beta.49 should set resolutions
to beta.49, while beta.43 should forces a beta.43?
Yes @vicary
I'm still seeing this TypeScript error with @apollo/client
3.0.0-rc.2.
"@apollo/client": "3.0.0-rc.2",
"@apollo/link-context": "^2.0.0-beta.3",
"@apollo/link-error": "^2.0.0-beta.3",
"@apollo/link-retry": "^2.0.0-beta.3",
Any known workarounds now with the RC?
same with
"@apollo/client": "^3.0.0-rc.4",
"@apollo/link-context": "^2.0.0-beta.3",
Same with
"@apollo/client": "^3.0.0-rc.4",
"@apollo/link-context": "^2.0.0-beta.3",
They opened a can of worm on maintaining cross dependency of a common core package, when it really should be as simple as a peer dependency. What a shame.
is there any fix?
i tried "resolution" 49 beta trip mentioned above, didn't work for me
import Cookie from 'js-cookie';
import config from 'config';
import {
ApolloClient,
createHttpLink,
InMemoryCache,
ApolloLink,
} from '@apollo/client';
import { WebSocketLink } from '@apollo/link-ws';
import { setContext } from 'apollo-link-context';
import { onError } from '@apollo/link-error';
const wsLink = new WebSocketLink({
uri: config.server.graphQLWs,
options: {
reconnect: true,
},
});
const httpLink = createHttpLink({
uri: config.server.graphQLApi,
});
const cache = new InMemoryCache({
addTypename: false,
});
const errorLink: any = onError(({ graphQLErrors, networkError }) => {
if (graphQLErrors) {
graphQLErrors.forEach(({ message, locations, path }) =>
console.log(
`[GraphQL error]: Message: ${message}, Location: ${locations}, Path: ${path}`
)
);
}
if (networkError) {
console.log(`[Network error]: ${networkError}`);
}
});
const authLink: any = setContext((_, { headers, ...context }) => {
// get the authentication token from local storage if it exists
const token = Cookie.get('userToken');
// return the headers to the context so httpLink can read them
return {
headers: {
...headers,
authorization: token ? `Bearer ${token}` : '',
},
...context,
};
});
const hasSubscriptionOperation = ({ query: { definitions } }) =>
definitions.some(
({ kind, operation }) =>
kind === 'OperationDefinition' && operation === 'subscription'
);
export const client = new ApolloClient({
version: '1',
resolvers: {},
cache: cache,
link: ApolloLink.from([
errorLink,
authLink,
ApolloLink.split(hasSubscriptionOperation, wsLink, httpLink),
]),
queryDeduplication: false,
});
that's my fix
I still have this issue with @apollo/client:3.0.2
I still have this issue with @apollo/client:3.1.0
My issue was with the ApolloProvider. After upgrading to. @apollo/client 3.1.3 i was still importing ApolloProvider from @apollo/react-hooks. Simply imported it from @apollo/client instead and everything works fine.
I am using "@apollo/client": "^3.1.3" and still have same issue. Any update ?
The solution for me was to use the links included in @apollo/client
instead of the @apollo/link-*
packages. See the part about links in the migration docs for more information:
https://www.apollographql.com/docs/react/migrating/apollo-client-3-migration/#apollo-link-
@afroald It seems working now 馃挴
@afroald's solution didn't work for me. Same error.
"dependencies": {
"@apollo/client": "3.1.3",
"@apollo/link-context": "2.0.0-beta.3",
"@apollo/link-error": "2.0.0-beta.3",
...
}
and
import { HttpLink, ApolloClient, ApolloLink, InMemoryCache } from '@apollo/client';
import { setContext } from '@apollo/link-context';
import { onError } from '@apollo/link-error';
interface ProviderProps {
apiUrl?: string;
ibpCookie?: string;
}
function TitanGqlClient({ apiUrl, ibpCookie }: ProviderProps) {
const httpLink = new HttpLink({
uri: apiUrl || 'http://localhost:4000/graphql',
});
const authLink: any = setContext(
(_, { headers }) => ibpCookie && {
headers: {
...headers,
'X-Cookie': ibpCookie,
},
},
);
const errorLink: any = onError(({ graphQLErrors, networkError }) => {
if (graphQLErrors) {
// eslint-disable-next-line no-console
graphQLErrors.map(({ message, locations, path }) => console.log(
`[GraphQL error]: Message: ${message}, Location: ${locations}, Path: ${path}`,
),
);
}
if (networkError) {
// eslint-disable-next-line no-console
console.log(`[Network error]: ${networkError}`);
}
});
const link = ApolloLink.from([errorLink, authLink.concat(httpLink)]);
const client = new ApolloClient({
link,
cache: new InMemoryCache(),
});
return client;
}
export { TitanGqlClient };
Shows
src/components/App.tsx(13,25): error TS2322: Type 'ApolloClient<NormalizedCacheObject>' is not assignable to type 'ApolloClient<any>'.
The types of 'link.split' are incompatible between these types.
Type '(test: (op: import("/Users/foo/sandbox-docker/bar/node_modules/@cloudvox/titan-core-components/node_modules/@apollo/client/link/core/types").Operation) => boolean, left: import("/Users/foo/sandbox-docker/bar/node_modules/@cloudvox/titan-core-components/node_modules/@apollo/client/link/c...' is not assignable to type '(test: (op: import("/Users/foo/sandbox-docker/bar/node_modules/@apollo/client/link/core/types").Operation) => boolean, left: import("/Users/foo/sandbox-docker/bar/node_modules/@apollo/client/link/core/ApolloLink").ApolloLink | import("/Users/foo/sandbox-docker/bar/node_mo...'.
Types of parameters 'left' and 'left' are incompatible.
Type 'import("/Users/foo/sandbox-docker/bar/node_modules/@apollo/client/link/core/ApolloLink").ApolloLink | import("/Users/foo/sandbox-docker/bar/node_modules/@apollo/client/link/core/types").RequestHandler' is not assignable to type 'import("/Users/foo/sandbox-docker/bar/node_modules/@cloudvox/titan-core-components/node_modules/@apollo/client/link/core/ApolloLink").ApolloLink | import("/Users/foo/sandbox-docker/bar/node_modules/@cloudvox/titan-core-components/node_modules/@apollo/client/link/core/types").RequestHand...'.
Type 'ApolloLink' is not assignable to type 'ApolloLink | RequestHandler'.
Type 'import("/Users/foo/sandbox-docker/bar/node_modules/@apollo/client/link/core/ApolloLink").ApolloLink' is not assignable to type 'import("/Users/foo/sandbox-docker/bar/node_modules/@cloudvox/titan-core-components/node_modules/@apollo/client/link/core/ApolloLink").ApolloLink'.
Types of property 'split' are incompatible.
Type '(test: (op: import("/Users/foo/sandbox-docker/bar/node_modules/@apollo/client/link/core/types").Operation) => boolean, left: import("/Users/foo/sandbox-docker/bar/node_modules/@apollo/client/link/core/ApolloLink").ApolloLink | import("/Users/foo/sandbox-docker/bar/node_mo...' is not assignable to type '(test: (op: import("/Users/foo/sandbox-docker/bar/node_modules/@cloudvox/titan-core-components/node_modules/@apollo/client/link/core/types").Operation) => boolean, left: import("/Users/foo/sandbox-docker/bar/node_modules/@cloudvox/titan-core-components/node_modules/@apollo/client/link/c...'.
Types of parameters 'left' and 'left' are incompatible.
Type 'import("/Users/foo/sandbox-docker/bar/node_modules/@cloudvox/titan-core-components/node_modules/@apollo/client/link/core/ApolloLink").ApolloLink | import("/Users/foo/sandbox-docker/bar/node_modules/@cloudvox/titan-core-components/node_modules/@apollo/client/link/core/types").RequestHand...' is not assignable to type 'import("/Users/foo/sandbox-docker/bar/node_modules/@apollo/client/link/core/ApolloLink").ApolloLink | import("/Users/foo/sandbox-docker/bar/node_modules/@apollo/client/link/core/types").RequestHandler'.
Type 'ApolloLink' is not assignable to type 'ApolloLink | RequestHandler'.
@dep-dt You should import like this and that is what @afroald mentioned above.
import { onError } from "@apollo/client/link/error";
import { setContext } from "@apollo/client/link/context";
Remove those packages @apollo/link-context
and @apollo/link-error
as those are already included in the @apollo/client
@khanakia This worked thanks. Also thanks @afroald :)
I still have this issue with: "@apollo/client": "^3.1.4"
....
const wsLink = new WebSocketLink({
uri: `ws://localhost:3000/graphql`,
options: {
reconnect: true,
},
});
const httpLink = createHttpLink({
uri: 'http://localhost:3000/graphql',
});
const authLink = setContext((_, { headers }) => {
// get the authentication token from local storage if it exists
const token = localStorage.getItem('accessToken');
console.log(token);
// return the headers to the context so httpLink can read them
return {
headers: {
...headers,
authorization: token ? `Bearer ${token}` : '',
},
};
});
const client = new ApolloClient({
link: from([authLink.concat(httpLink), wsLink]), //~> first one will work but second one (wsLink)
cache: new InMemoryCache(),
});
@benyou1969
I think at least part of your problem is the construction of createHttpLink
... This has changed recently. Try something like this:
import { HttpLink } from '@apollo/client';
import { setContext } from '@apollo/client/link/context';
const httpLink = new HttpLink({
uri: 'http://localhost:3000/graphql',
});
const authLink = setContext((_, { headers }) => {
// get the authentication token from local storage if it exists
const token = localStorage.getItem('accessToken');
console.log(token);
// return the headers to the context so httpLink can read them
return {
headers: {
...headers,
authorization: token ? `Bearer ${token}` : '',
},
};
});
Here's a full working example of mine, if it helps anyone who ends up here.
import { HttpLink, ApolloClient, ApolloLink, InMemoryCache } from '@apollo/client';
import { setContext } from '@apollo/client/link/context';
import { onError } from '@apollo/client/link/error';
import { RetryLink } from '@apollo/client/link/retry';
const httpLink = new HttpLink({
uri: 'http://localhost:4000/graphql',
});
// we are appending a custom cookie
const authLink = setContext(
(_, { headers }) => fooCookie && {
headers: {
...headers,
'X-Cookie': fooCookie,
},
},
);
const errorLink = onError(({ graphQLErrors, networkError }) => {
if (graphQLErrors) {
graphQLErrors.map(({ message, locations, path }) => console.log(
`[GraphQL error]: Message: ${message}, Location: ${locations}, Path: ${path}`,
));
}
if (networkError) {
console.log(`[Network error]: ${networkError}`);
}
});
// Prevent ApolloClient from retrying infinitely on failures
// Doc: https://www.apollographql.com/docs/link/links/retry/
const retryLink = new RetryLink({
delay: {
initial: 300,
max: 20000,
jitter: true,
},
attempts: {
max: 3,
retryIf: error => !!error,
},
});
const link = ApolloLink.from([errorLink, retryLink, authLink, httpLink]);
const client = new ApolloClient({
link,
cache: new InMemoryCache(),
});
@dep-dt First Thank you for replying.
I tried it but still get the same result, I think the problem is when I try to add wsLink
and httpLink
to link:
I can't use both of them, always going to run the first one only.
or
According to this article I see
"Apollo Client should use your WebSocketLink for subscriptions, but it shouldn't use it for queries or mutations. For those operations, Apollo Client should use HTTP as usual. To support this, the @apollo/client library provides a split function that lets you use one of two different Links, according to the result of a boolean check."
So maybe you need to do something like this?
import { split, HttpLink } from '@apollo/client';
import { getMainDefinition } from '@apollo/client/utilities';
import { WebSocketLink } from '@apollo/client/link/ws';
const wsLink = new WebSocketLink({
uri: `ws://localhost:3000/graphql`,
options: {
reconnect: true,
},
});
const httpLink = new HttpLink({
uri: 'http://localhost:3000/graphql',
});
const authLink = setContext((_, { headers }) => {
// get the authentication token from local storage if it exists
const token = localStorage.getItem('accessToken');
// return the headers to the context so httpLink can read them
return {
headers: {
...headers,
authorization: token ? `Bearer ${token}` : '',
},
};
});
// The split function takes three parameters:
//
// * A function that's called for each operation to execute
// * The Link to use for an operation if the function returns a "truthy" value
// * The Link to use for an operation if the function returns a "falsy" value
const splitLink = split(
({ query }) => {
const definition = getMainDefinition(query);
return (
definition.kind === 'OperationDefinition' &&
definition.operation === 'subscription'
);
},
wsLink,
httpLink,
);
const client = new ApolloClient({
link: ApolloLink.from([splitLink, authLink]),
cache: new InMemoryCache(),
});
Apologies in advance, I've not worked with WebSocketLink
much.
According to this article I see
"Apollo Client should use your WebSocketLink for subscriptions, but it shouldn't use it for queries or mutations. For those operations, Apollo Client should use HTTP as usual. To support this, the @apollo/client library provides a split function that lets you use one of two different Links, according to the result of a boolean check."
So maybe you need to do something like this?
import { split, HttpLink } from '@apollo/client'; import { getMainDefinition } from '@apollo/client/utilities'; import { WebSocketLink } from '@apollo/client/link/ws'; const wsLink = new WebSocketLink({ uri: `ws://localhost:3000/graphql`, options: { reconnect: true, }, }); const httpLink = new HttpLink({ uri: 'http://localhost:3000/graphql', }); const authLink = setContext((_, { headers }) => { // get the authentication token from local storage if it exists const token = localStorage.getItem('accessToken'); // return the headers to the context so httpLink can read them return { headers: { ...headers, authorization: token ? `Bearer ${token}` : '', }, }; }); // The split function takes three parameters: // // * A function that's called for each operation to execute // * The Link to use for an operation if the function returns a "truthy" value // * The Link to use for an operation if the function returns a "falsy" value const splitLink = split( ({ query }) => { const definition = getMainDefinition(query); return ( definition.kind === 'OperationDefinition' && definition.operation === 'subscription' ); }, wsLink, httpLink, ); const client = new ApolloClient({ link: ApolloLink.from([splitLink, authLink]), cache: new InMemoryCache(), });
Apologies in advance, I've not worked with
WebSocketLink
much.
@dep-dt Dude thank you very much for taking the time to help me find the solution,
and I worked when I switched
const client = new ApolloClient({
link: ApolloLink.from([splitLink, authLink]),
cache: new InMemoryCache(),
});
to
const client = new ApolloClient({
link: ApolloLink.from([authLink, splitLink]),
cache: new InMemoryCache(),
});
thanks again!
@benyou1969 great news!
Most helpful comment
@dep-dt You should import like this and that is what @afroald mentioned above.
Remove those packages
@apollo/link-context
and@apollo/link-error
as those are already included in the@apollo/client