graphql-request icon indicating copy to clipboard operation
graphql-request copied to clipboard

[WIP] feat(wip:subsciption): Introducing subscription

Open jjangga0214 opened this issue 4 years ago • 7 comments

🚧 Work In Progress - Introducing subscription

This aims to resolve #77.

Background

The GraphQL specification deliberately does not specify the transport layer.

Currently, WebSocket and HTTP are chosen by the majority, with these two different approaches.

  1. Full WebSocket: query, mutation, and subscription all on WebSocket.
  2. Hybrid: HTTP for query and mutation, while WebSocket for subscription. (Though theoretically arbitrary protocol can be chosen, there isn't another combination adopted wide enough.)

Opinion

As graphql-request has used HTTP for query and mutation, I assume the "Hybrid" approach is suitable for subscription, at least for now.

Protocol

For GraphQL over WebSocket, two different protocols exist, suggested by the community and adopted wide enough.

A. subscriptions-transport-ws (protocol spec): This is a legacy and not actively maintained, but many servers have implemented it. B. graphql-ws (protocol spec): This is newly suggested one.

Those two are not compatible.

Thus graphql-request lets users choose one over the other by passing options.

Implementation (Pseudo Code)

class GraphQLClient {
  
  subscribe(
    payload: SubscribePayload,
    options: SubscribeOptions
  ): () => void
  
  unsubscribeAll(): void
}

subscribe returns a function that a consumer should call to unsubscribe.

SubscribeOptions includes Observer proposed by tc39/proposal-observable, which defines how to process received data, error, and completion.

TODOs

  • [ ] Add more fields on SubscribeOptions
  • [ ] Refactor constructor of the class GraphQLClient for configuring subscription options.
  • [ ] Create a facade function for users to import solely
  • [ ] Write docs
  • [ ] Test

jjangga0214 avatar Jul 09 '21 11:07 jjangga0214

are there any updates on this feature? this is something my team would really like, and i'd be happy to contribute if it's something the maintainers definitely want in a future release

davevanfleet avatar Jan 25 '22 19:01 davevanfleet

@davevanfleet

if it's something the maintainers definitely want in a future release

This repo is active, but the maintainers do not develop/express priority for the suggested features. They only review PRs.

If you/your team need this feature, would you contribute?

jjangga0214 avatar Jan 26 '22 09:01 jjangga0214

@jjangga0214 i'd be happy to. i'll start taking a look over the weekend. i don't see a contributing guide in the repo, would you like me to open a PR against this feature branch when i have something ready?

davevanfleet avatar Jan 26 '22 15:01 davevanfleet

Happy to help too

Eduardoveras avatar Jan 28 '22 19:01 Eduardoveras

Happy to help when/how I can (limited time) the community drive this. As long as things are reasonable we'll merge it in. Clearly, adding support for a part of the GraphQL spec (subscriptions) is reasonable :). Maybe there will be some specific discussions around the implementation but can already say at a high level this is welcome.

jasonkuhrt avatar Jan 28 '22 19:01 jasonkuhrt

@davevanfleet @Eduardoveras Great.

First, how about your creating suggestions on schema of SubscribePayload and SubscribeOptions?

Of course, if our opinions do not converge, you can create distinct PRs anytime, but I think this branch is a good starting point for now.

I wish you to create PRs against my branch, which is github.com/jjangga0214/graphql-request:feat/subscription.

jjangga0214 avatar Jan 29 '22 12:01 jjangga0214

@jjangga0214 would it make sense for the payload to use the RequestDocument type, and we can just validate that it's actually using a subscription query?

For SubscribeOptions the two options that come to mind for me right away are variables of type OperationVariables, and an onDataRecieved callback function.

I'd imagine an actual use of the subscribe function to look something like:

const { data, unsubscribe } = await client.subscribe(
  JOBS_SUBSCRIPTION,
  { 
    variables: {search: { starts_date: "whatever", ends_date: "whenever" } },
    onDataRecieved: (data) => doSomething(),
  } 
)

davevanfleet avatar Jan 30 '22 03:01 davevanfleet