While implementing Server-Sent Events (SSE) using
Fastify, I ran into a subtle but important gotcha: using
reply.raw
, bypasses Fastify’s built-in middleware and response handling. This
happens because reply.raw
gives you direct access to Node.js’s native
http.ServerResponse
object.
The Scenario
I was setting up an SSE endpoint, and naturally reached for the low-level
reply.raw
to manually manage the connection and keep it open:
fastify.get("/events", (req, reply) => {
reply.raw.writeHead(200, {
"Content-Type": "text/event-stream",
"Cache-Control": "no-cache",
Connection: "keep-alive",
});
res.write(`data: hello\n\n`);
});
This seemed fine at first, but when I tested it in the browser, the frontend was throwing CORS errors, and the SSE connection wasn’t working properly.
It turned out that since I was using reply.raw
, Fastify’s CORS middleware was
not applying the Access-Control-Allow-Origin
header. That header is required
for the browser to accept the connection.
What You’re Bypassing
By using reply.raw
, you’re skipping:
- Fastify’s
reply.send()
, which handles serialisation - Any plugin-added headers or logic (e.g. compression, CORS, etc.)
- Lifecycle hooks like
onSend
oronResponse
In this case, the missing CORS header was the issue. Without it, the browser rejected the connection.
The Safer Alternative
If you don’t need low-level control, stick with the built-in reply
API:
fastify.get("/standard-endpoint", (req, reply) => {
reply.code(200).send({
hello: "world",
});
});
This ensures:
- Proper
Content-Type
headers - JSON serialisation
- Compatibility with plugins like CORS
If you do use reply.raw
, be aware that you’re responsible for setting
everything yourself, including security-related headers like CORS.
Takeaway
Using reply.raw
gives you full control over the response, but it also means
you are opting out of Fastify’s default behaviors. That includes things like
automatic headers, serialisation, and plugin support. Make sure to account for
that if you run into unexpected behavior, especially with browser-facing
features like SSE or CORS.