I set up a SSE endpoint on a simple Fastify project. This is the endpoint:
app.get('/sse', async (_, res) => {
res.raw.setHeader('Content-Type', 'text/event-stream')
res.raw.setHeader('Cache-Control', 'no-cache')
res.raw.setHeader('Connection', 'keep-alive')
const updatesHashMap = {}
const sendUpdate = async (_) => {
const devices = await Device.getAll()
const updates = await Update.updateData()
console.log(new Date())
for (const update of updates) {
if (devices.some(device => device.id === update.deviceId)) {
updatesHashMap[update.deviceId] = { ...update, online: !hasAMinutePassed(update.createdAt) }
}
}
return res.raw.write(`data: ${JSON.stringify(updatesHashMap)}\n\n`)
}
const intervalId = setInterval(() => {
sendUpdate({
title: 'Devices',
devices: updatesHashMap
})
}, 1000)
res.raw.on('close', () => {
clearInterval(intervalId)
})
})
done()
}
In the frontend I have this connection logic with a subscriber happening
<script>
let eventSource;
function connectToSSE() {
eventSource = new EventSource('/sse');
eventSource.onmessage = (event) => {
const data = JSON.parse(event.data);
for (const device in data) {
document.getElementById(`free-mem-${device}`).innerText = `${data[device].freeMem}Mb`;
document.getElementById(`online-status-${device}`).innerText = `${data[device].online ? "Online" : "Offline" }`;
document.getElementById(`load-${device}`).innerText = `${data[device]?.load}%`;
document.getElementById(`uptime-${device}`).innerText = `${data[device]?.uptime}m`;
document.getElementById(`updated-at-${device}`).innerText = data[device].createdAt;
}
};
eventSource.onerror = (error) => {
console.error('SSE Error:', error);
};
}
connectToSSE();
setInterval(() => {
eventSource.close();
connectToSSE();
}, 5000);
</script>
Every 5 seconds I got an error in the logs that looks like this:
{"level":50,"time":1698804029401,"pid":159,"hostname":"82b3f69fc8b4","reqId":"req-3c","err":{"type":"FastifyError","message":"Promise may not be fulfilled with 'undefined' when statusCode is not 204","stack":"FastifyError: Promise may not be fulfilled with 'undefined' when statusCode is not 204\n at /app/node_modules/fastify/lib/wrapThenable.js:30:30\n at process.processTicksAndRejections (node:internal/process/task_queues:95:5)","name":"FastifyError","code":"FST_ERR_PROMISE_NOT_FULFILLED","statusCode":500},"msg":"Promise may not be fulfilled with 'undefined' when statusCode is not 204"}
Now, I figured out that the error is related to the client disconnecting from the backend but I cannot find what is the problem with the unsubscribing proccess.
I also noticed that sometimes the client just doesnt disconnect.
This is all happening in the same Fastify app, im serving the frontend with a .eta file from within the app.
I tried already a lot of things regarding the async methods and trying not to leave chunks of code that have no return statement but so far I couldnt catch where the problem relies.