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

graphql over rsocket spec #327

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open

Conversation

OlegDokuka
Copy link
Member

Adds a separate dedicated extension spec that describes integration with GraphQL protocol

Motivation:

GraphQL is a popular API protocol that does not has not direct binding to a specific transport. Thus, it is going to be valuable to add an addition that clearly explains how such protocol should be handled when it is run over RSocket


1. If `REQUEST_STREAM` request does not contain `subscription` GraphQL operation, such request must be rejected with an `ERROR` having `INVALID` as an error code.
2. If `REQUEST_RESPONSE` request does not contain `query` or `mutation` GraphQL operation, such request must be rejected with an `ERROR` having `INVALID` as an error code.
3. Hand

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hanging bullet item

Copy link
Member

@yschimke yschimke left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. Seems appropriate for an incubating spec.


## Introduction

GraphQL protocol is one of the popular API protocol for building modern edge-network communication. This extension specification provides an interoperable structure for metadadata payloads and sub-specification which defines graphql communication over RSocket protocol.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit

Suggested change
GraphQL protocol is one of the popular API protocol for building modern edge-network communication. This extension specification provides an interoperable structure for metadadata payloads and sub-specification which defines graphql communication over RSocket protocol.
GraphQL protocol is one of the popular API protocols for building modern edge-network communication. This extension specification provides an interoperable structure for metadadata payloads and sub-specification which defines graphql communication over RSocket protocol.


## Handling GraphQL operation

A GraphQL operation can be handled if it is agreed during the RSocket `SETUP` phase and specified in the data-mime type `application/graphql` or if composite metadata enabled, specified in the REQUEST PAYLOAD data mime-type `application/graphql` via [Stream Data MIME Types Metadata Extension](/PerStreamDataMimeTypesDefinition.md).
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this inline with https://graphql.org/learn/serving-over-http/, which defines that mime type? Should we reference that?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The spec draft is the place to look, for GraphQL over HTTP. The general practice today is to use application/json or also application/graphql+json although the latter is not all that common and is not set in stone. I see the latest draft has changed that to application/graphql-response+json.

I gather that the intent with application/graphql is to set up expectations for the use of GraphQL over RSocket but the data-mime type is really about the format to use and not about choosing a higher level protocol (like sub-protocol in WebSocket). I think clients will simply need to know that a particular RSocket endpoint is for GraphQL when connecting, and the data-mime type is about agreeing on the format of payloads (e.g. application/json).


### Streaming operation
To handle `subscription` GraphQL operation a `REQUESTER` MUST issue a `REQUEST_STREAM` payload.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess it never got standardised but do any potential backends support live queries?

graphql/graphql-spec#386

https://medium.com/open-graphql/graphql-subscriptions-vs-live-queries-e38302c7ab8e

Mainly because this is where facebooks use of rsocket came from.


### Handling Graphql errors

GraphQL errors must be encoded as a normal RSocket `PAYLOAD` with `NEXT` semantic followed by `PAYLOAD` with `COMPLETE` flag (onNext(ERROR_MESSAGE) -> onComplete()). RSocket Protocol `ERROR` must be treated as unexpected interruption of the subscription and MUST not contain any GraphQL data in the payload.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't seem right to me.

Compare to https://graphql.org/learn/serving-over-http/#response and https://spec.graphql.org/June2018/#sec-Errors

The spec does say "Event streams may complete in response to an error or simply because no more events will occur." so I'm happy if you know it's correct.

Isn't it valid that a stream has different errors per response, and continues?

Or is this a more serious error like a 501 in HTTP? Maybe this just needs some distinction like request level errors?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is a distinction between a "request error" and a "partial response".

A request error happens before a GraphQL request is accepted for execution by the GraphQL engine (e.g. parse error, query syntax error, security error, etc.) in which case the whole request fails. I think this should result in an ERROR frame.

Once the GraphQL engine starts execution, the response is considered valid but potentially partial. That is, there may be failures for individual fields (e.g. couldn't fetch the user's LinkedIn profile) which is reflected with a null value + a field specific error in the response. This is called a partial response. A subscription may have such partial responses but those do not mean the subscription ends.

Copy link
Contributor

@gabis-precog gabis-precog left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM


## Handling GraphQL operation

A GraphQL operation can be handled if it is agreed during the RSocket `SETUP` phase and specified in the data-mime type `application/graphql` or if composite metadata enabled, specified in the REQUEST PAYLOAD data mime-type `application/graphql` via [Stream Data MIME Types Metadata Extension](/PerStreamDataMimeTypesDefinition.md).
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nitpick:

Suggested change
A GraphQL operation can be handled if it is agreed during the RSocket `SETUP` phase and specified in the data-mime type `application/graphql` or if composite metadata enabled, specified in the REQUEST PAYLOAD data mime-type `application/graphql` via [Stream Data MIME Types Metadata Extension](/PerStreamDataMimeTypesDefinition.md).
A GraphQL operation can be handled, if it is agreed upon during the RSocket `SETUP` phase by specifying the data mime-type as `application/graphql` or if composite metadata is enabled and the data mime-type in the REQUEST PAYLOAD is specified as `application/graphql` via [Stream Data MIME Types Metadata Extension](/PerStreamDataMimeTypesDefinition.md).


## Handling GraphQL operation

A GraphQL operation can be handled if it is agreed during the RSocket `SETUP` phase and specified in the data-mime type `application/graphql` or if composite metadata enabled, specified in the REQUEST PAYLOAD data mime-type `application/graphql` via [Stream Data MIME Types Metadata Extension](/PerStreamDataMimeTypesDefinition.md).
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The spec draft is the place to look, for GraphQL over HTTP. The general practice today is to use application/json or also application/graphql+json although the latter is not all that common and is not set in stone. I see the latest draft has changed that to application/graphql-response+json.

I gather that the intent with application/graphql is to set up expectations for the use of GraphQL over RSocket but the data-mime type is really about the format to use and not about choosing a higher level protocol (like sub-protocol in WebSocket). I think clients will simply need to know that a particular RSocket endpoint is for GraphQL when connecting, and the data-mime type is about agreeing on the format of payloads (e.g. application/json).


### Single result operation

To handle `query` or `mutation` GraphQL operation a `REQUESTER` MUST issue a `REQUEST_RESPONSE` payload.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
To handle `query` or `mutation` GraphQL operation a `REQUESTER` MUST issue a `REQUEST_RESPONSE` payload.
To send a GraphQL request with a `query` or `mutation` operation, a `REQUESTER` MUST use an RSocket `REQUEST_RESPONSE` interaction.

GraphQL operation lifecycle is bound to the RSocket REQUEST RESPONSE operation semantic and must follow it as specified by RSocket protocol.

### Streaming operation
To handle `subscription` GraphQL operation a `REQUESTER` MUST issue a `REQUEST_STREAM` payload.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
To handle `subscription` GraphQL operation a `REQUESTER` MUST issue a `REQUEST_STREAM` payload.
To send a GraphQL request with a `subscription` operation, a `REQUESTER` MUST use a `REQUEST_STREAM` interaction.


### Handling Graphql errors

GraphQL errors must be encoded as a normal RSocket `PAYLOAD` with `NEXT` semantic followed by `PAYLOAD` with `COMPLETE` flag (onNext(ERROR_MESSAGE) -> onComplete()). RSocket Protocol `ERROR` must be treated as unexpected interruption of the subscription and MUST not contain any GraphQL data in the payload.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is a distinction between a "request error" and a "partial response".

A request error happens before a GraphQL request is accepted for execution by the GraphQL engine (e.g. parse error, query syntax error, security error, etc.) in which case the whole request fails. I think this should result in an ERROR frame.

Once the GraphQL engine starts execution, the response is considered valid but potentially partial. That is, there may be failures for individual fields (e.g. couldn't fetch the user's LinkedIn profile) which is reflected with a null value + a field specific error in the response. This is called a partial response. A subscription may have such partial responses but those do not mean the subscription ends.

### Handling unexpected

1. If `REQUEST_STREAM` request does not contain `subscription` GraphQL operation, such request must be rejected with an `ERROR` having `INVALID` as an error code.
2. If `REQUEST_RESPONSE` request does not contain `query` or `mutation` GraphQL operation, such request must be rejected with an `ERROR` having `INVALID` as an error code.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These are not really possible to validate upfront. When a GraphQL request is received, it is not yet parsed. Once it's handed to the GraphQL engine, it does the parsing and decided how to process.

Once the GraphQL engine produces a response, e.g. graphql.ExecutionResult, we can check if the "data" in that is a Publisher, and only then can we validate something like this.

There maybe other ways to hook this, e.g. through a graphq.execution.Instrumentation but generally I don't know if we really need this validation.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There maybe other ways to hook this, e.g. through a graphql.execution.Instrumentation but generally I don't know if we really need this validation.

Is this specifically a GraphQL Java construct? If so I would hesitant to include anything in this sub-spec that is only reasonably achievable by a single Language or existing libraries for a specific language.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

8 participants