Docs
Launch GraphOS Studio

HTTP callback protocol for GraphQL subscriptions

For federated subgraphs communicating with the Apollo Router


This reference describes a protocol for (or subgraphs) to send data to a subscribing graph router (such as the ) via HTTP callbacks. Use this reference if you're adding support for this protocol to a library or other system.

For with many simultaneous open , this protocol scales better than WebSocket-based protocols, which require long-lasting open connections.

The provides support for this protocol as part of its support for federated :

SubgraphApollo Router(subscriber)SubgraphApollo Router(subscriber)New data availableNew data availableRegisters subscription and callback URL over HTTPSends new data via HTTPto callback URLSends new data via HTTPto callback URL

Protocol flow

All actions described in these steps are performed by one of Router, Subgraph, or Emitter.

Emitter is a system that sends new data to Router. Emitter is commonly also Subgraph, but it doesn't have to be.

Initialization

  1. Before it executes a , Router generates a unique ID to represent that .

  2. Router sends a to Subgraph via HTTP POST using the standard GraphQL request payload with Accept header containing application/json;callbackSpec=1.0:

    {
    "query": "subscription { userWasCreated { name reviews { body } } }",
    "extensions": {
    "subscription": {
    "callbackUrl": "http://localhost:4000/callback/c4a9d1b8-dc57-44ab-9e5a-6e6189b2b945",
    "subscriptionId": "c4a9d1b8-dc57-44ab-9e5a-6e6189b2b945",
    "verifier": "XXX",
    "heartbeatIntervalMs": 5000
    }
    }
    }

    The extensions property of this payload includes a subscription object with the following:

    • callbackUrl: The URL that Emitter will send data to
    • subscriptionId: The generated unique ID for the
    • verifier: A string that Emitter will include in all HTTP callback requests to verify its id
    • heartbeatIntervalMs: The number of milliseconds a heartbeat has to be sent to the callback endpoint for the , if 0 it means the heartbeat is disabled
  3. Before Subgraph responds to Router's request, it sends a check message with callback protocol version header (subscription-protocol: callback/1.0) to the provided callbackUrl for confirmation from Router:

    {
    "kind": "subscription",
    "action": "check",
    "id": "c4a9d1b8-dc57-44ab-9e5a-6e6189b2b945",
    "verifier": "XXX"
    }

    All messages sent to callbackUrl are HTTP POST requests. Message types are documented below.

    This helps ensure that Subgraph is able to send callbacks successfully, and that the id and verifier are correct.

  4. Router validates the check message using its id and verifier .

    Again, Subgraph has not yet responded to Router's original request!

    • If the check message is valid, Router responds with the following details:
      • A 204 HTTP status code
      • An empty response body
      • The response header that specifies the maximum supported protocol version subscription-protocol: callback/1.0
    • If the check message is invalid, Router responds with a status code besides 204 (400-level recommended).
      • If this occurs, Subgraph then responds to Router's request with a 400-level status code, and the is canceled.
  5. If validation succeeds, Subgraph spawns a background process or notifies a separate system (Emitter) to begin listening for events.

  6. Subgraph finally responds to Router with a 200-level status code and empty data response ({ "data": null }). This indicates to Router that the has been initialized.

With initialization complete, the protocol commences its main loop.

Main loop

The protocol's main loop remains active for the duration of the . During the main loop, all of the following occur:

  • If heartbeats are enabled (heartbeatIntervalMs > 0), within every period of heartbeatIntervalMs milliseconds, Emitter must send a check message to Router to confirm that Router is still listening. (Note, the value of heartbeatIntervalMs is set by the initial payload sent in extensions from the Router.)
  • Whenever new data is available, Emitter sends a next message to Router containing the new data.
  • If an error occurs and the must be terminated, Emitter sends a complete message to Router and includes the errors .
  • If the reaches the end of its stream and no new data is forthcoming, Emitter sends a complete message to Router and omits the errors .
  • If Router terminates a particular , it should return a 404 status code for all future HTTP callbacks sent for that subscription.
    • Relatedly, if Emitter receives a 404 status code from Router for an HTTP callback, it should consider the associated terminated.

Message types

During the protocol flow, Subgraph and Emitter send various messages to Router's callback URL via HTTP POST requests. All HTTP requests should include callback protocol version header (subscription-protocol: callback/1.0).

All of these messages include the following base properties in their JSON body:

{
"kind": "subscription",
"action": "check",
"id": "c4a9d1b8-dc57-44ab-9e5a-6e6189b2b945",
"verifier": "XXX"
}
PropertyDescription
kind

This value is currently always subscription.

action

The message type, which is one of the following:

  • check
  • next
  • complete

The example body above is for a check message.

Router should respond with an error if this value is not one of the above.

id

The identifier for the message's associated , generated by Router during protocol initialization.

Router should respond with a 404 status code if this value does not match the ID of an active .

verifier

A string value provided by Router during initialization so it can validate callback requests from Subgraph and Emitter.

Router should respond with an error if this does not match the value it provided when initializing the with the corresponding id.

and behaviors specific to individual message types are below.

check

During protocol initialization, Subgraph sends a synchronous check message to Router to help ensure that it can send callbacks successfully, and that the id and verifier provided by Router are correct.

As long as is active and heartbeat is enabled (heartbeatIntervalMs > 0), Emitter must send a check message to Router every heartbeatIntervalMs milliseconds (value coming from the initial payload sent in extensions from the Router). This enables Emitter to confirm both that it can still reach Router's callback endpoint, and that is still active.

A check message includes only base message fields:

{
"kind": "subscription",
"action": "check",
"id": "c4a9d1b8-dc57-44ab-9e5a-6e6189b2b945",
"verifier": "XXX"
}

If id and verifier both match Router's provided values, Router should respond with the following details:

  • A 204 HTTP status code
  • An empty response body
  • The response header that specifies the maximum supported protocol version subscription-protocol: callback/1.0

Otherwise, Router should respond with an error and should terminate the associated .

During initialization, Subgraph must send this message synchronously. That's because it sends this message to Router to confirm a request from Router, before responding to that request.

next

Whenever a new event occurs, Emitter sends the associated data to Router in a next message.

The next message includes a payload , which contains the data in standard JSON response format:

{
"kind": "subscription",
"action": "next",
"id": "c4a9d1b8-dc57-44ab-9e5a-6e6189b2b945",
"verifier": "XXX",
"payload": {
"data": {
"numberIncremented": 5
}
}
}

complete

Emitter sends a complete message to Router to terminate an active . Emitter might terminate a for the following reasons:

  • The has reached the end of its stream and no new data is forthcoming.
  • An Emitter error occurred that caused the to fail.

A complete message can include an errors containing an array of errors. This field is required if the failed and optional if it completed successfully (it's usually an empty list in this case):

{
"kind": "subscription",
"action": "complete",
"id": "c4a9d1b8-dc57-44ab-9e5a-6e6189b2b945",
"verifier": "XXX",
"errors": [{ // Optional if subscription completed successfully
"message": "Something went wrong"
}]
}

On receiving a complete message, Router terminates the associated .

Error states

The following are common error states that can occur with this protcol:

  • Emitter can't communicate with Router's callback endpoint, either because the endpoint isn't available or because its provided credentials (id and/or verifier) are invalid.
  • Emitter receives an error HTTP status code from Router's callback endpoint.
    • If the error code is 404, Emitter should consider the associated terminated by Router.
    • In other error cases, Emitter should consider the terminated due to an unexpected error.
  • Emitter fails to send Router a check message for an active every heartbeatIntervalMs milliseconds (value coming from the initial payload sent in extensions from the Router), causing Router to terminate that .
Previous
Subscriptions setup
Next
Client protocol: HTTP multipart
Edit on GitHubEditForumsDiscord

© 2024 Apollo Graph Inc.

Privacy Policy

Company