subscriptions-transport-ws is no longer maintained apollo client recommends graphql-ws #1351
-
Hey just curious any plans to upgrade subscriptions dependency to use recommended module?
https://github.com/apollographql/subscriptions-transport-ws |
Beta Was this translation helpful? Give feedback.
Replies: 2 comments
-
From my understanding, this vue-apollo module its just an adapter to easily integrate graphql features into our vue components, therefore, if we want to use "graphql-ws" to create our subscription links, we just need to use the correct libraries that apollo already offers and set them up in our main file. What I think it actually needs to update, is the documentation since that one have examples that uses "WebSocketLink" which uses "subscriptions-transport-ws" instead of "GraphQLWsLink" which uses "graphql-ws": https://v4.apollo.vuejs.org/guide-composable/subscription.html#client-setup Here is an example of a graphql wsLink using the new protocol in a vue project created from scratch with npm init vue@latest: import App from "./App.vue";
import router from "./router";
import { createApp, provide, h } from "vue";
import { DefaultApolloClient } from "@vue/apollo-composable";
import {
HttpLink,
split,
ApolloClient,
InMemoryCache,
} from "@apollo/client/core";
import { GraphQLWsLink } from "@apollo/client/link/subscriptions"; // <-- This one uses graphql-ws
import { createClient } from "graphql-ws";
import { getMainDefinition } from "@apollo/client/utilities";
import "./assets/main.css";
// Create an http link:
const httpLink = new HttpLink({
uri: "http://localhost:4000/graphql",
});
const wsLink = new GraphQLWsLink(
createClient({
url: "ws://localhost:4000/graphql",
})
);
// using the ability to split links, you can send data to each link
// depending on what kind of operation is being sent
const link = split(
// split based on operation type
({ query }) => {
const definition = getMainDefinition(query);
return (
definition.kind === "OperationDefinition" &&
definition.operation === "subscription"
);
},
wsLink,
httpLink
);
// Create the apollo client with cache implementation
const apolloClient = new ApolloClient({
link,
cache: new InMemoryCache(),
});
const app = createApp({
setup() {
provide(DefaultApolloClient, apolloClient);
},
render: () => h(App),
});
app.use(router);
app.mount("#app"); And these are the dependencies "dependencies": {
"@apollo/client": "^3.7.4",
"@vue/apollo-composable": "^4.0.0-beta.1",
"graphql": "^16.6.0",
"graphql-tag": "^2.12.6",
"graphql-ws": "^5.11.2",
"vue": "^3.2.45",
"vue-router": "^4.1.6"
} To figure that out, I had to take as reference the instructions for react in the Apollo documentation: https://www.apollographql.com/docs/react/data/subscriptions |
Beta Was this translation helpful? Give feedback.
-
Thanks, For subscriptions I ended up using Pusher protocol with Soketi & Laravel which works over websocket. import {
ApolloLink,
Observable,
NextLink,
Operation,
FetchResult,
Observer,
} from '@apollo/client/core';
import Pusher from 'pusher-js';
interface PusherLinkOptions {
pusher: Pusher;
}
// interface SubscriptionDataResponse {
// extensions?: {
// lighthouse_subscriptions: {
// channel: string
// }
// }
// }
// Inspired by https://github.com/rmosolgo/graphql-ruby/blob/master/javascript_client/src/subscriptions/PusherLink.ts
export class PusherLink extends ApolloLink {
private pusher: Pusher;
constructor(options: PusherLinkOptions) {
super();
this.pusher = options.pusher;
}
request(
operation: Operation,
forward?: NextLink
): Observable<FetchResult> | null {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const subscribeObservable = new Observable((_observer) => {
//
});
// Capture the super method
const prevSubscribe =
subscribeObservable.subscribe.bind(subscribeObservable);
// Override subscribe to return an `unsubscribe` object, see
// https://github.com/apollographql/subscriptions-transport-ws/blob/master/src/client.ts#L182-L212
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
subscribeObservable.subscribe = (observerOrNext, onError, onComplete) => {
prevSubscribe(observerOrNext, onError, onComplete);
const observer = this.getObserver(observerOrNext, onError, onComplete);
let subscriptionChannel: string;
if (forward) {
forward(operation).subscribe({
next: (data) => {
// If the operation has the subscription channel, it's a subscription
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
subscriptionChannel =
data?.extensions?.lighthouse_subscriptions.channel ?? null;
// No subscription found in the response, pipe data through
if (!subscriptionChannel) {
//console.log('No subscription found in response');
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
observer.next(data);
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
observer.complete();
return;
}
this.subscribeToChannel(subscriptionChannel, observer);
},
});
}
// Return an object that will unsubscribe_if the query was a subscription
return {
closed: false,
unsubscribe: () => {
subscriptionChannel &&
this.unsubscribeFromChannel(subscriptionChannel);
},
};
};
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
return subscribeObservable;
}
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
subscribeToChannel(subscriptionChannel: string, observer: Observer<any>) {
console.log('subscribing to channel ' + subscriptionChannel);
this.pusher
.subscribe(subscriptionChannel)
.bind('lighthouse-subscription', (payload: any) => {
if (!payload.more) {
this.unsubscribeFromChannel(subscriptionChannel);
observer.complete?.();
}
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
const result = payload.result;
if (result) {
observer.next?.(result);
}
});
}
unsubscribeFromChannel(subscriptionChannel: string) {
this.pusher.unsubscribe(subscriptionChannel);
}
private getObserver<T>(
observerOrNext: Observer<T> | ((v: T) => void),
error?: (e: Error) => void,
complete?: () => void
) {
if (typeof observerOrNext === 'function') {
return {
next: (v: T) => observerOrNext(v),
error: (e: Error) => error && error(e),
complete: () => complete && complete(),
};
}
return observerOrNext;
}
} Used something like this. const pusherOptions = {
authEndpoint: SUBSCRIPTION_AUTH_ENDPOINT,
httpHost: SUBSCRIPTION_HTTP_HOST,
httpPath: SUBSCRIPTION_HTTP_PATH,
wsHost: SUBSCRIPTION_WS_HOST,
wsPort: SUBSCRIPTION_WS_PORT,
forceTLS: false,
// auth: {
// headers: {
// authorization: BEARER_TOKEN,
// },
// },
enabledTransports: [ 'ws', 'wss' ]
};
const pusherLink = new PusherLink({
pusher: new Pusher(PUSHER_API_KEY, pusherOptions),
});
const link = ApolloLink.from([
retryLink,
errorHandlerLink,
authMiddleware,
pusherLink,
httpLink,
]); |
Beta Was this translation helpful? Give feedback.
From my understanding, this vue-apollo module its just an adapter to easily integrate graphql features into our vue components, therefore, if we want to use "graphql-ws" to create our subscription links, we just need to use the correct libraries that apollo already offers and set them up in our main file. What I think it actually needs to update, is the documentation since that one have examples that uses "WebSocketLink" which uses "subscriptions-transport-ws" instead of "GraphQLWsLink" which uses "graphql-ws": https://v4.apollo.vuejs.org/guide-composable/subscription.html#client-setup
Here is an example of a graphql wsLink using the new protocol in a vue project created from scratch with…