Comments (6)
Just to mention that we had a similar issue today, with fastify-cors
. The headers set by this plugin was added to the fastify.reply
and not to fastify.reply.raw
, and sendNodeResult
was ignoring those.
Doing that before calling sendNodeResult
should work as a temporary workaround:
for (const [key, value] of Object.entries(res.getHeaders())) {
res.raw.setHeader(key, String(value || ''))
}
from graphql-helix.
I checked out how different plugins handle sending responses with res.raw.write
:
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 result
returned from processRequest
, could implement that interface (if it is a stream response and not only a single payload). Then for fastify we actually might not need to use res.raw
at all!
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
from graphql-helix.
I'm a bit biased because it's what I'm using right now, but check out graphql-ez
it uses graphql-helix
and has several integrations, not only for fastify
, but express
, koa
and more.
Here's the fastify
integration
Hope it helps in some way!
from graphql-helix.
@dotansimha
Thank you for elaborating that and it makes sense to keep it framework agnostic.
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:
import Fastify from 'fastify'; // yarn add fastify
import fastifyWebsocket from 'fastify-websocket'; // yarn add fastify-websocket
import { makeHandler } from 'graphql-ws/lib/use/fastify-websocket';
import { schema } from './previous-step';
const fastify = Fastify();
fastify.register(fastifyWebsocket);
fastify.get('/graphql', { websocket: true }, makeHandler({ schema }));
fastify.listen(4000, (err) => {
if (err) {
fastify.log.error(err);
return process.exit(1);
}
console.log('Listening to port 4000');
});
from graphql-helix.
Hi @taneba ,
Thanks for reaching out. Currently, sendResult
is using the raw request because we wanted it to be agnostic to the HTTP server.
I think in cases where you want to use different setup, you might need to use the code from sendResult
and adjust to to your needs (and use fastify and not the raw HTTP).
I'm not sure how to address this, because it seems specific for fastify?
@n1ru4l thoughts?
from graphql-helix.
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!
from graphql-helix.
Related Issues (20)
- Avoid bundling GraphiQL HOT 3
- processRequest does not support multiple accept types HOT 2
- FastifyError: Promise may not be fulfilled with 'undefined' when statusCode is not 204 HOT 1
- Prohibiting SSE via POST breaks `graphql-sse` usage (since 1.11.0) HOT 6
- Fastify example - cannot set cookies HOT 5
- missing exports `send ... ResponseResult` from `node-http` in the barrel file
- Error: Cannot find module `<path>\node_module\graphql-helix\dist\dist.js` HOT 3
- Helix in Gateway to pass headers down to subschema? HOT 1
- graphql-ws example: Invalid query returns "Subscriptions should be sent over WebSocket" HOT 2
- Slow processRequest when error thrown HOT 1
- graphql-koa-example: subscriptions not working
- [Question] Have you thought about graphql-jit? HOT 2
- Adding `extensions` to `GraphQLParams`
- Is development dead? Zero commits since The Guild announced Yoga 2. HOT 1
- Infinite loop when using defer and async iterators (`JavaScript heap out of memory`)
- Can we detect/support cancelation of @stream operations?
- typescript compilation errors
- Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client HOT 1
- Before and after crud operation listeners
- Enabling CSP protection with koa + @koa/helmet causes 404 error
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from graphql-helix.