GraphQLSwift / Graphiti

The Swift GraphQL Schema framework for macOS and Linux
MIT License
531 stars 67 forks source link

Example of using subscriptions with Vapor #77

Closed cshadek closed 2 years ago

cshadek commented 2 years ago

Has anyone tried this? I assume we have to use Vapor's websocket API. Is that correct?

NeedleInAJayStack commented 2 years ago

Yes, I'm doing it successfully. Here's a really rough example that I haven't tested at all but it shows the main steps:

func routes(_ app: Application, apiBuilder: OpenAPIBuilder) throws {
    routes.webSocket(
        "graphqlSubscribe",
        onUpgrade: { request, websocket in
            let api = API()
            let context = Context(db: app.db)
            let disposeBag = DisposeBag()
            websocket.onText { _, message in
                let json = message.data(using: .utf8)!
                let graphQLRequest = try! JSONDecoder().decode(GraphQLRequest.self, from: json)
                api.subscribe(
                    request: graphQLRequest.query,
                    context: context,
                    on: request.eventLoop,
                    variables: graphQLRequest.variables,
                    operationName: graphQLRequest.operationName
                ).map { result in
                    let streamOpt = result.stream!
                    let stream = streamOpt as! ObservableSubscriptionEventStream
                    let observable = stream.observable
                    observable.subscribe(
                        onNext: { resultFuture in
                            resultFuture.whenSuccess { result in
                                websocket.send(try! JSONEncoder().encode(result))
                            }
                        },
                        onCompleted: { _ in
                            websocket.close()
                        }
                    ).disposed(by: disposeBag)
                }
            }
        }
    )
}

Basically,

  1. Set up a websocket endpoint
  2. When you receive input from that endpoint, decode it and push it through to your graphQL subscription
  3. When the subscription provides results, encode it and push it back through the websocket

Granted there's a lot more work to do, like conforming to subprotocols, error handling, memory leaks, etc. but this should hopefully get you started.

cshadek commented 2 years ago

I ended up using a library called Pioneer that hides away some of the GraphiQL, websocket, and other hosting logic. It also has a bunch of extensions to make Graphiti have async await support and even the subscriptions are asyncstreams too.

https://github.com/d-exclaimation/pioneer