-
Notifications
You must be signed in to change notification settings - Fork 48
Usage with fastify middleware #75
Comments
Hi @taneba , I'm not sure how to address this, because it seems specific for fastify? @n1ru4l thoughts? |
@dotansimha How about taking an approach like what graphql-ws is doing, which is to have a helper function for certain library. Their example for fastify:
|
Just to mention that we had a similar issue today, with Doing that before calling for (const [key, value] of Object.entries(res.getHeaders())) {
res.raw.setHeader(key, String(value || ''))
} |
I checked out how different plugins handle sending responses with export const plugin: FastifyPluginAsync<SsePluginOptions> = async function(
instance,
options
): Promise<void> {
instance.decorateReply("sse", function(
this: FastifyReply,
source: AsyncIterable<EventMessage>
): void {
Object.entries(this.getHeaders()).forEach(([key, value]) => {
this.raw.setHeader(key, value);
});
this.raw.setHeader("Content-Type", "text/event-stream");
this.raw.setHeader("Connection", "keep-alive");
this.raw.setHeader("Cache-Control", "no-cache,no-transform");
this.raw.setHeader("x-no-compression", 1);
this.raw.write(serializeSSEEvent({ retry: options.retryDelay || 3000 }));
toStream(transformAsyncIterable(source)).pipe(this.raw);
});
}; The solution you proposed @dotansimha seems to be the correct one! We should probably only give users the headers and the payload. The responsibility for glueing that to the framework of their choice should be more or less straightforward and can be guided with tutorials, recipes and example applications. One of the main reasons why we added these helpers was boilerplate code we had to write over and over again for the multi-part protocol. Now we realize that we tried to own too much... Instead of owning the whole pipeline we could only have helper functions for formatting the payload into the desired protocol and yield the string partials to the user: async function* multiPartProtocol(
source: AsyncIterable<ExecutionResult>,
transformResult: TransformResultFn
) {
yield "---";
for await (const result of source) {
const chunk = Buffer.from(JSON.stringify(transformResult(result)), "utf8");
const data = [
"",
"Content-Type: application/json; charset=utf-8",
"Content-Length: " + String(chunk.length),
"",
chunk
];
if (result.hasNext) {
data.push("---");
}
yield "\r\n";
}
yield "\r\n-----\r\n";
} async function* sseProtocol(
source: AsyncIterable<ExecutionResult>,
transformResult: TransformResultFn
) {
for await (const result of source) {
yield `data: ${JSON.stringify(transformResult(result))}\n\n`;
}
} Furthermore, I spotted that Fastify can handle stream payloads 🤔 https://github.com/fastify/fastify/blob/0439d5ffcab1709659353e8633a9a1ff36595c22/lib/reply.js#L426-L431 SO maybe the From a user-land perspective the code would look like this: app.route({
method: ["GET", "POST"],
url: "/graphql",
async handler(req, res) {
const request = {
body: req.body,
headers: req.headers,
method: req.method,
query: req.query,
};
if (shouldRenderGraphiQL(request)) {
res.type("text/html");
res.send(renderGraphiQL({}));
} else {
const request = {
body: req.body,
headers: req.headers,
method: req.method,
query: req.query,
};
const { operationName, query, variables } = getGraphQLParameters(request);
const { headers, source } = await processRequest({
operationName,
query,
variables,
request,
schema,
});
// headers determined by helix
for (const header of headers) {
res.setHeader(header.key, header.value)
}
// source is a stream or string
res.send(source)
}
},
}); There is still an open question for me: Should we even tell people what headers/protocols they should use? #161 |
I'm a bit biased because it's what I'm using right now, but check out Here's the Hope it helps in some way! |
Any further thoughts on this? This was incredible confusing to figure out. I'm using fastify-cors and this took hours to figure out that the helix setup was swallowing the headers. @dotansimha answer worked for me! |
Is there a way to make
sendResult
work with fastify middleware?for example:
In the code above, I want fastifyCors to add access-control-allow-origin header to the response, but it won't.
I'm not completely sure but is that because
sendResult
is handling "RawResponse" rather than "Response" which is extended by fastify?The text was updated successfully, but these errors were encountered: