2

I have an issue with subscription can't be unsubscribe.

Before we start, this is my setup: Apollo Client(graphql-ws) <-> Apollo Server(graphql-ws). On the server, I build a custom PubSub instead of using the one provided.

As you can see here, the client has sent a complete request to server with the id. However, the server is still sending more data to it. I have read somewhere that you have to send GQL_STOP, aka STOP instead. However, this is what Apollo Client is sending.

A bit of code:

Client subscription:

export const useGetDataThroughSubscription = (
    resourceIds: number[],
    startDate?: Date,
    endDate?: Date
) => {
    const variables = {
        startTime: startDate?.toISOString() ?? '',
        endTime: endDate?.toISOString() ?? '',
        resourceIds,
    };

    return useGetDataSubscription({
        variables,
        ...
    })
}

Server pubsub:

const createPubSub = <TopicPayload extends { [key: string]: unknown }>(
    emitter: EventEmitter = new EventEmitter()
) => ({
    publish: <Topic extends Extract<keyof TopicPayload, string>>(
        topic: Topic,
        payload: TopicPayload[Topic]
    ) => {
        emitter.emit(topic as string, payload);
    },
    async *subscribe<Topic extends Extract<keyof TopicPayload, string>>(
        topic: Topic,
        retrievalFunc: (value: TopicPayload[Topic]) => Promise<any>
    ): AsyncIterableIterator<TopicPayload[Topic]> {
        const asyncIterator = on(emitter, topic);
        for await (const [value] of asyncIterator) {
            const data = await retrievalFunc(value);
            yield data;
        }
    },

Server subscribe to event:

const resolver: Resolvers = {
    Subscription: {
        [onGetAllLocationsEvent]: {
            async *subscribe(_a, _b, ctx) {
                const locations = await ...;

                yield locations;

                const iterator = ctx.pubsub.subscribe(
                    onGetAllLocationsEvent,
                    async (id: number) => {
                        const location = ...;

                        return location;
                    }
                );

                for await (const data of iterator) {
                    if (data) {
                        yield [data];
                    }
                }
            },
            resolve: (payload) => payload,
        },
    },
};

In this one, if instead of the for loop, I return iterator instead, then the server will send back a complete and stop the subscription all together. That's great, but I want to keep the connection open until client stop listening.

And server publish

ctx.pubsub.publish(onGetAllResourcesEvent, resource.id);

So how should I deal with this?

Tree Nguyen
  • 1,198
  • 1
  • 14
  • 37

0 Answers0