-
-
Notifications
You must be signed in to change notification settings - Fork 7.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Allow sending custom headers and status codes on a SSE controller when an async flow is required #12260
Comments
Why can't you use rxjs |
Hey @kamilmysliwiec, thank you so much for your time :)
I could but I would hazard to say that would not solve the issue. Even if I use those excellent tools, headers are flushed just after the observable is returned. This is done before any asyncronous operation finishes. Nest won't wait for any asyncronous operation at all before sending those headers. Let's supose we go that way, then
The Observable pattern is great but asumes we always want to start an alive connection. To ilustrate the possible solution, consider the following change at public async sse<
TInput extends Observable<unknown> = any,
TResponse extends WritableHeaderStream = any,
TRequest extends IncomingMessage = any,
>(
result: TInput | Promise<TInput>,
response: TResponse,
request: TRequest,
options?: { additionalHeaders: AdditionalHeaders },
) {
// It's possible that we sent headers already so don't use a stream
if (response.writableEnded) {
return;
}
let observableResult: TInput;
if (isPromise(result)) {
observableResult = await result;
} else {
observableResult = result;
}
this.assertObservable(observableResult);
const stream = new SseStream(request);
stream.pipe(response, options);
// Code continues using observableResult instead of result
} This way an asyncronous flow could decide to append additional headers to the response or even set a different status code. |
After sucessfully implementing an abstraction that allows my NestJS framework layer interact with a normal NestJS controller to serve a SSE endpoint, let me add some caveats to consider before implementing this feature in the core package:
The implementation on my side was a little bit tedious but pretty straightforward. I hope we find the right abstraction in order to go for this feature so no one needs to reinvent the wheel again. |
Using
|
This solution would work but I don't think creating a new controller with every request is what we are looking for here. My cup of tea would be solving the issue instead of trying to workaround it. I expect an sse controller to handle the endpoint instead of instantiating a new controller with every request. |
Agree with you here. Just wanted to share a workaround for those who need something today. |
I see. To give my 5 cents, if someone want something sort of battle proven in the backend side, I could probably do something about it since I already implemented a decent approach in one of my monorepos:
Some additional pieces are required, but I know I could extract a less opinionated (and simpler) library and publish it to npm. If enough people like this idea I don't mind going for it. If someone likes the idea, feel free to react to this message. My cup of tea would be adding the library on the nest monorepo, but I am aware I'll probably need to publish it in another repo and npm org. |
Is there an existing issue that is already proposing this?
Is your feature request related to a problem? Please describe it
I've been doing a PoC involving the SSE feature.
The abstraction looks good, working with Observables feel like a nice way to representing an SSE based flow. Having said that, I really don't know how I should implement a non 200 HTTP status code in some scenarios.
After reading the spec, I would say it's perfectly fine to send non 200 status codes, the spec even states that redirections are allowed:
Having a look to the original issue that let to the SSE feature (#4826), it seems this can be done accesing the response:
It seems I could probably set the status code in the same way I set a header, but I would say this is sort of tricky: it only allow us to set headers / status code if we do this in a syncronous flow, before the observable result is returned.
Having a look at the nest repository, it seems the headers are send as soon as the observable object is returned by the controller handler:
Having a look at
packages/core/router/router-response-controller.ts
:When piping the SSE stream, it seems headers are flushed whatsoever:
Having a look at
packages/core/router/sse-stream.ts
So, keeping all of this in mind, I cannot see a way of sending a non 200 http status code nor some custom headers if I require an async flow to determine these headers / status code.
A simple example would be a SSE endpoint to subscribe to the events happening on a resource. Let's say I want to implement a
GET v1/games/{gameId}/events
SSE endpoint. I expect this endpoint to return a 404 status code if the{gameId}
game resource does not exist. In order to accomplish that, I would need to search my game resource in my data source, an operation it's very likely to be asyncronous. By the way I would had fetched my data source response, the headers would had been flushed, so it would be too late to send my 404 status code in case no game is found.Describe the solution you'd like
I would love an abstraction that lets the developer do an async flow in order to determine custom headers / status codes.
Maybe this is a terrible approach, but allowing SSE controller handlers to return a
Promise<Observable<MessageEvent>>
could be a simple, non breaking change solution. Sending anObservable<MessageEvent>
would still be allowed. The idea would be waiting for the observable promise to be fullfilled so the sse handler can set custom headers / custom status codes. Once the promise is fullfilled, headers would be flushed.Teachability, documentation, adoption, migration strategy
In case of going this way, SSE doc page should be updated to reflect the possibilty of sending a
Promise<Observable<MessageEvent>>
and some examples should be added.The migration strategy would be trivial imo due to the fact this is not a breaking change. Sending an
Observable<MessageEvent>
would still be allowed.What is the motivation / use case for changing the behavior?
The lack of an approach to set custom headers / status code in the scenario I described in the previous section of the issue.
The text was updated successfully, but these errors were encountered: