Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[integrations/fastify] Add documentation to enable SSE subscriptions #3276

Open
EmrysMyrddin opened this issue May 17, 2024 Discussed in #3273 · 4 comments
Open

[integrations/fastify] Add documentation to enable SSE subscriptions #3276

EmrysMyrddin opened this issue May 17, 2024 Discussed in #3273 · 4 comments
Labels
kind/docs Improvements or additions to documentation

Comments

@EmrysMyrddin
Copy link
Collaborator

Discussed in #3273

Originally posted by santino May 15, 2024
Hello folks, as mentioned by the title, I am looking to setup subscriptions in my Yoga implementation.
I am not using Yoga as a full server, but instead I am integrating it with my fastify instance.

The useGraphQLSSE plugin only seems to work if Yoga is executed as a server.
Do you have any example for writing up a plugin that only handles subscription requests received on my single /graphql endpoint?

@EmrysMyrddin EmrysMyrddin added the kind/docs Improvements or additions to documentation label May 17, 2024
@santino
Copy link

santino commented May 19, 2024

I ended up building this plugin to handle subscriptions in my Fastify setup

import { EOL } from 'os'

export const useSubscription = () => {
  return {
    async onSubscribe () {
      return {
        async onSubscribeResult ({
          result,
          args: {
            contextValue: { req, reply }
          }
        }) {
          req.socket.on('end', () => {
            result.return()
            reply.hijack() // tell Fastify to skip the automatic invocation of reply.send()
          })
          req.socket.on('close', () => {
            reply.log.info(
              {
                res: reply,
                responseTime: reply.elapsedTime
              },
              'request completed'
            )
            reply.raw.end() // tell the server that all of the response headers and body have been sent
          })

          for await (const data of result) {
            reply.raw.write(
              `event: next${EOL}data: ${JSON.stringify(data)}${EOL}${EOL}`
            )
          }
        }
      }
    }
  }
}

Hopefully this is useful to other devs as well.

@ardatan
Copy link
Collaborator

ardatan commented May 19, 2024

GraphQL Yoga doesn't need an extra plugin for SSE except single connection mode. You can see Fastify tests below that use the same path for subscriptions. Maybe you can help us reproducing your issue here;
https://github.com/dotansimha/graphql-yoga/blob/main/examples/fastify/__integration-tests__/fastify.spec.ts
GraphQL SSE plugin only handles single connection mode of GraphQL SSE protocol, and you can configure the path of that listens the subscriptions;
https://github.com/dotansimha/graphql-yoga/blob/main/packages/plugins/graphql-sse/src/index.ts#L17

I'd not use that kind of plugin which bypasses entire Yoga plugin hooks, and it might cause an unexpected behavior.

@santino
Copy link

santino commented May 20, 2024

You are absolutely right.
Thanks for your comment. I looked at the test and decided I had to make my implementation work in the correct way.

The issue I had initially was related to how I was setting an initial value for my subscription.
I was doing a pubSub.subscribe and then used a process.nextTick to create the first event for my subscription topic.
It was a convoluted solution with some drawbacks (had to make sure the "artificial" event would be issued only to the new subscriber), but it worked.

After migrating to Yoga the process.nextTick was somehow issued before the subscriber was actually subscribed to the pubSub. Not sure why, but this caused my "artificial" event to not be delivered. Because of this I wrongly assumed that this setup wasn't working.

Now I spent more time into this and figured all this out.
So I removed my custom useSubscription plugin entirely and switched to a much better solution to deliver my initial data to the subscription with this:

Repeater.merge([
  initialData,
  pubSub.subscribe(topic)
])

I think I would probably add something to the documentation that documents simple subscriptions without any need for the SSE plugin. Maybe make it more explicit.
In any case, this issue can be closed.

@EmrysMyrddin
Copy link
Collaborator Author

Thank you for your feedback!

It seems our documentation still need some update on this ? If it's the case, I will let this issue open to not forget about it :-)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
kind/docs Improvements or additions to documentation
Projects
None yet
Development

No branches or pull requests

3 participants