Apollo v4 compatibility? #119
-
|
Has anyone managed to get this working with Apollo v4? I've migrated our app across but unfortunately the SSE side of things now just produce 400 Bad Request errors. I'm a little lost on where to begin to diagnose. Any help would be much appreciated. |
Beta Was this translation helpful? Give feedback.
Replies: 4 comments
-
|
Can you share some code, like the setup or ideally a reproduction so that we have all the context? |
Beta Was this translation helpful? Give feedback.
-
|
Hi, I can share some code initially, it may take me a bit longer to try to get a working reproduction together, to make some endpoints accessible externally. This is all the code switched over to apollo v4 api (AFAIK). In v4 all the ApolloLinks became classes, and so i'm wondering if this has changed something. apollo-sse.ts import {
ApolloLink,
Observable,
} from '@apollo/client';
import { print, type FormattedExecutionResult } from 'graphql';
import { type Client, type Sink } from 'graphql-sse';
export class GraphQLSSELink extends ApolloLink {
constructor (public readonly client: Client) {
super();
}
public request (operation: ApolloLink.Operation): Observable<ApolloLink.Result> {
return new Observable((observer) => {
return this.client.subscribe<ApolloLink.Result>(
{ ...operation, query: print(operation.query) },
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
{
next: observer.next.bind(observer),
complete: observer.complete.bind(observer),
error: observer.error.bind(observer),
// casting around a wrong type in graphql-sse, which incorrectly expects `Sink<ExecutionResult>`
} satisfies Sink<FormattedExecutionResult> as any,
);
});
}
}apollo.ts import {
ApolloClient,
InMemoryCache,
CombinedGraphQLErrors,
ServerError,
HttpLink,
ApolloLink } from '@apollo/client';
import { getMainDefinition } from '@apollo/client/utilities';
import { SetContextLink } from '@apollo/client/link/context';
import { ErrorLink } from '@apollo/client/link/error';
import { RetryLink } from '@apollo/client/link/retry';
import { createClient } from 'graphql-sse';
import { LocalState } from '@apollo/client/local-state';
import { GraphQLSSELink } from 'src/apollo-sse';
// Create an Http link:
const httpLink: ApolloLink = new HttpLink({
uri: 'http://localhost:9080/app/graphql',
credentials: 'same-origin',
headers: {
Origin: '/',
},
fetchOptions: {
mode: 'cors',
credentials: 'same-origin',
},
});
// Create an SSE link:
const sseLink: ApolloLink = new GraphQLSSELink(createClient({
uri: 'http://localhost:9080/app/graphql',
retryAttempts: Infinity,
}));
const authLink: ApolloLink = new SetContextLink(({ headers }): Partial<ApolloLink.OperationContext> => {
return {
headers: {
...headers,
},
};
});
const splitLink: ApolloLink = ApolloLink.split(
({ query }) => {
const definition = getMainDefinition(query);
return (
definition.kind === 'OperationDefinition' && definition.operation === 'subscription'
);
},
sseLink,
httpLink,
);
const retryLink: ApolloLink = new RetryLink({
delay: {
initial: 300,
max: Infinity,
jitter: true,
},
attempts: {
max: 5,
retryIf: (error) => !!error,
},
});
export default new ApolloClient({
link: ApolloLink.from([
retryLink,
authLink,
splitLink,
]),
cache: new InMemoryCache(),
localState: new LocalState(),
}); |
Beta Was this translation helpful? Give feedback.
-
|
Just an update on this. I believe I have now got this working. The issue appears to be that Apollo now includes an The working code for the link is now: apollo-sse.ts import {
ApolloLink,
Observable,
} from '@apollo/client';
import { print, type FormattedExecutionResult } from 'graphql';
import { type Client, type Sink } from 'graphql-sse';
export class GraphQLSSELink extends ApolloLink {
constructor (public readonly client: Client) {
super();
}
public request (operation: ApolloLink.Operation): Observable<ApolloLink.Result> {
const { query, variables, operationName, extensions } = operation;
return new Observable((observer) => {
return this.client.subscribe<ApolloLink.Result>(
{
variables,
operationName,
extensions,
query: print(query),
},
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
{
next: observer.next.bind(observer),
complete: observer.complete.bind(observer),
error: observer.error.bind(observer),
// casting around a wrong type in graphql-sse, which incorrectly expects `Sink<ExecutionResult>`
} satisfies Sink<FormattedExecutionResult> as any,
);
});
}
} |
Beta Was this translation helpful? Give feedback.
-
|
Looks like Apollo eventually hit the same issue here apollographql/apollo-client#12950 |
Beta Was this translation helpful? Give feedback.
Just an update on this. I believe I have now got this working. The issue appears to be that Apollo now includes an
operationTypeprop on theoperationpassed to the request, which then gets rejected by the server. This means you can't prop spread the operation to the SSE link and instead have to destructure it to ensure you only pass known/valid props.The working code for the link is now:
apollo-sse.ts