Each payload is just appended as a condensed JSON (no newlines!) payload, with a newline at the end:
HTTP/1.1 200 OK
Content-Type: application/graphql-response+jsonl
Cache-Control: no-cache
{"data":{...},"hasNext":true}
{"incremental":[...],"hasNext":true}
{}
{"incremental":[...],"hasNext":true}
{"hasNext":false}
{}
is a keepalive message.
The parser for JSONL would be trivial-keep accumulating data until you hit a \n
,
then parse that as JSON and throw it away.
Incredibly easy to implement for clients.
We would need to add any multiplexing information to the response payloads themselves.
Here's an example of an incredibly trivial parser for it (may or may not work, I wrote it direct into GitHub):
async function handleEvents(url, cb) {
const response = await fetch(url);
const decoder = new TextDecoderStream();
const stream = response.body.pipeThrough(decoder);
let current = '';
for await (const chunk of stream) {
current += chunk;
let i;
while ((i = current.indexOf("\n")) >= 0) {
const json = JSON.parse(current.substring(0, i));
current = current.substring(i + 1);
if (Object.keys(json).length === 0) {
// Keepalive
} else {
cb(json);
}
}
}
}
For HTTP/1.1, for greater efficiency you could use Transfer-Encoding: chunked
such that the connection is not terminated once the request is finished. I don't believe this is a concern in HTTP2+.