IBM / openapi-to-graphql

Translate APIs described by OpenAPI Specifications (OAS) into GraphQL
https://developer.ibm.com/open/projects/openapi-to-graphql/
MIT License
1.6k stars 207 forks source link

How to implement graphql subscriptions using oasgraph? #80

Open ryankauk opened 5 years ago

ryankauk commented 5 years ago

Really interested in utilizing graphql with loopback 4 and am wondering if it's possible to implement subscriptions?

ErikWittern commented 5 years ago

From an OASGraph point-of-view, the support of subscriptions depends on support for expressing streams / pub-sub like mechanisms in the OAS. The notion of callbacks and related callback objects may be the right thing to look at, but still the debate around this seems not to have concluded yet.

Apart from that, this question seems to be specifically about LoopBack 4 also. Maybe @raymondfeng or @dhmlau know more about subscription / stream / pub-sub support in LoopBack 4?

dhmlau commented 5 years ago

@ryankauk, we're looking into supporting messaging/eventing style APIs. Please see our story in https://github.com/strongloop/loopback-next/issues/1884. @raymondfeng has a PoC: https://github.com/raymondfeng/loopback4-example-websocket.

dhmlau commented 5 years ago

Actually this one might fit more what you're looking for: https://github.com/strongloop/loopback4-example-kafka. (cross posting from https://github.com/strongloop/loopback-next/issues/1925)

ErikWittern commented 5 years ago

Related to #101

mitar commented 4 years ago

I am looking into using GraphQL for Kubernetes API, which has a way of watching resources. I think that could be directly translated to GraphQL subscriptions. But it seems this package is currently not really supporting it. Maybe also because how Kubernetes describes its API looks like it simple provides watch parameter, and mark that the response can be application/json;stream=watch. So it seems this package should somehow now that if one makes a subscribe GraphQL, it should set watch parameter to true and then interpret application/json;stream=watch correctly?

mitar commented 4 years ago

After more looking into this, I think that yes, OpenAPI 3.0 callbacks should be converted to GraphQL subscriptions.

I think adding support for generating GraphQL schemas/types automatically from callbacks into GraphQL subscriptions would be a great first step. But it seems different API implementations are implementing callbacks differently. Some use websockets, some use HTTP2, it is pretty non-standard. So instead of waiting for world to find a standard way, I think easier would be to allow one to pass an implementation on how should a subscription be converted to the request (by maybe passing an event emitter factory, new event emitter for each subscription request). Then this package could use the schema plus event emmiter factory to expose those subscriptions.

ErikWittern commented 4 years ago

@mitar We looked into better subscription support in the past and found that OpenAPI is likely does not contain the required information for us to implement a generic solution. Some thoughts are captured in https://github.com/IBM/openapi-to-graphql/issues/101. The existence of alternative formats like https://www.asyncapi.com/ support this impression. I think you make the same point in your last comment, actually.

While allowing to pass in your own implementation, as you suggest, may address the issue, we currently want to focus on improving request-response flows (and are, for example, in the process of supporting wrapping the Kubernetes API better to that regard).

We are of course appreciative of PRs that could improve subscription support but - just to be fully transparent - it is not currently something we are actively pursuing ourselves.

mitar commented 4 years ago

OpenAPI is likely does not contain the required information for us to implement a generic solution

But it does enough for one to implement the schema for the subscription calls? So if I would have callback defined in OpenAPI 3.0, there is enough information to map that to the schema. The only missing piece is then how to handle such call and that would have to be custom because there is no standard way.

Am I missing something?

we currently want to focus on improving request-response flows

Any links of what are those plans?

and are, for example, in the process of supporting wrapping the Kubernetes API better to that regard

Oh, interesting, but what issues have you found there? It looked to me that (besides watching/subscription) otherwise the API seems pretty well supported already?

Currently Kubernetes API is not defined in terms of callbacks so one would have to anyway manually post-process schemas and remove watch parameter.

ErikWittern commented 4 years ago

@mitar Thanks for the prompt reply. To your questions:

Am I missing something?

I think you could be right, but am not very familiar with the callbacks option. Would it imply that the GraphQL server would listen at a (customizable) route for callbacks made by the REST-API server, and then forward these as subscriptions?

We are constantly improving schema-creation, so I think you are right, that logic can be reused for creating the subscription-related GraphQL types.

Any links of what are those plans?

We don't have an official "plan" for what items we want to tackle when, sorry! But we have been concerned with better support of anyOf and oneOf definitions for a while now, and the improvements should land soon: https://github.com/IBM/openapi-to-graphql/pull/287. These improvements will broaden the set of OpenAPI descriptions we support, including the Kubernetes OpenAPI, which uses these features.

mitar commented 4 years ago

Would it imply that the GraphQL server would listen at a (customizable) route for callbacks made by the REST-API server, and then forward these as subscriptions?

I looked into this more, and I think that this package could really do this automatically for all OpenAPI 3.0 compatible API endpoints. It would not work for Kubernetes at the moment (because Kubernetes is not yet using OpenAPI 3.0, nor defining callbacks, and having a custom approach to watching resources), but for standard compliant OpenAPI 3.0 implementations, we could do something pretty nice:

So, magic. It just works and makes subscriptions automatically. The proxy becomes slightly more complicated than just calling graphqlHTTP express, but it can work with all OpenAPI 3.0 compliant implementations. In the past I thought that OpenAPI 3.0 just defines the API for callbacks, but in fact it also defines how those callbacks should be made and so on, so we can do everything automatically.

A good side-effect of this is that now also clients behind NATs and firewalls can subscribe to events from the server. With OpenAPI 3.0 one has to have a public IP and can allow API to call back to them. This is pretty heavy limitation in my mind. The approach above addresses it.

For supporting Kubernetes watching, a manual schema post-processing seems will be necessary: to remove watch and other related parameters from queries, adding similar subscriptions, and then implementing with custom logic which uses watch.

getlarge commented 4 years ago

@Alan-Cha I think this one can be closed now ?

mitar commented 4 years ago

Just to provide context here. This has been done in version 2.1.0 in this PR: https://github.com/IBM/openapi-to-graphql/pull/311 See documentation here.

@getlarge @Alan-Cha Awesome work! I think this could be closed, but I think README section could have a part about subscriptions. I think it is a really cool feature to have it listed there.

mitar commented 4 years ago

So I have looked into the implementation and I think that current implementation is taking quite opinionated view on how REST server implements callbacks, namely, that is uses a pubsub service to do so. From the OpenAPI 3.0 spec and callbacks there, my understanding is that callbacks are HTTP request from the REST server back to the client. So an OpenAPI 3.0 callbacks example will not really work with current implementation here. openapi-to-graphql does not use HTTP to subscribe and does not define a callback endpoint for REST server to call back to (so the openapi-to-graphql server should listen to HTTP callbacks from the REST server).

So I would suggest we leave this issue open until also the HTTP based callbacks are supported and that it is not required that one augments the REST server/implementation itself.

Alan-Cha commented 4 years ago

@mitar I see what you mean and I think I largely agree. The current implementation is opinionated, enabling Subscription support by utilizing callbacks in a way that is different then how it was originally intended.

Regarding documentation, we will be adding a little bit more with #313 but I think more is needed.

To keep the discussion going, I will leave this issue open.

mitar commented 4 years ago

I do agree thought that what is currently made is probably a better overall design/architecture. Using HTTP for pub/sub what OpenAPI 3.0 defines is definitely less efficient than using a dedicated pubsub solution.

So maybe one way to address this gap is to have a stand-alone additional service which translates between HTTP backed callbacks and dedicated pubsub solution. So if one does not want to change their REST server (which uses HTTP callbacks) they can put in front this to-be-made service, which connects exposes a callback for the REST server to call into, subscribes as necessary to callbacks on the REST server, and pushes all calls into the dedicated pubsub solution. So maybe this does not have to be integrated directly with openapi-to-graphql but could be in addition to it.