Closed jakeorr closed 2 years ago
Latest commit: 0358b74facfa5d7d4f7cfcfeb0ea07690bd1372b
Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.
Click here to learn what changesets are, and how to add one.
Click here if you're a maintainer who wants to add a changeset to this PR
Merging #166 (0358b74) into master (14aa547) will not change coverage. The diff coverage is
100.00%
.
@@ Coverage Diff @@
## master #166 +/- ##
=========================================
Coverage 100.00% 100.00%
=========================================
Files 1 1
Lines 81 88 +7
=========================================
+ Hits 81 88 +7
Impacted Files | Coverage Δ | |
---|---|---|
src/index.js | 100.00% <100.00%> (ø) |
Continue to review full report at Codecov.
Legend - Click here to learn more
Δ = absolute <relative> (impact)
,ø = not affected
,? = missing data
Powered by Codecov. Last update 14aa547...0358b74. Read the comment docs.
Thanks for the handy library! Let me know if you would like anything further on the PR. 😄
Hi @hoangvvo. Happy New Year! Just wanted to check if you needed anything else on this change or had any further thoughts on it. Thanks!
Happy new year! Thanks for the PR and sorry for the late review. However, I don't think this breaking change is possible at this point. Since checking if response is sent is not really reliable (for cases like redirection, multiple res sent, multipart response, etc. and edge cases like race-condition etc.)
Happy new year! Thanks for the PR and sorry for the late review. However, I don't think this breaking change is possible at this point. Since checking if response is sent is not really reliable (for cases like redirection, multiple res sent, multipart response, etc. and edge cases like race-condition etc.)
No problem, I've been working off a fork, so it's not a big deal if this doesn't get merged.
To your point about checking if the response is sent, I hadn't thought about those details you mentioned. Is there a concern that isResSent
will return true when the response really hasn't been sent? If you're only worried about the other way around, where this change wouldn't detect the response as sent, then it's no different than it was previously. It wouldn't call done in the new spot and the existing code would continue working as it has been.
Sorry about the wait, I just have some time to revisit this PR today. I think I finally agree that this issue should be resolved. However, one concern is that sometimes I want to do some post processing after the response is sent. In that case, it would not really make sense to end the promise there. What do you think about that case. Example:
const handler = nc()
.get(async (req, res, next) => {
res.status(200).send('hello!');
next();
})
.all(async (req, res) => {
// it would have been resolved before this step
await logPostRequestToDatabase(req);
};
export default handler;
Sorry about the wait, I just have some time to revisit this PR today. I think I finally agree that this issue should be resolved. However, one concern is that sometimes I want to do some post processing after the response is sent. In that case, it would not really make sense to end the promise there. What do you think about that case. Example:
const handler = nc() .get(async (req, res, next) => { res.status(200).send('hello!'); next(); }) .all(async (req, res) => { // it would have been resolved before this step await logPostRequestToDatabase(req); }; export default handler;
No worries! Thanks for spending some time on it. Good point about running functions after the response is sent. My initial thought it to call next
instead of done
if the response is sent, but testing that, I'm seeing next called extra times which causes done
to be called multiple times. So, do we do something like this?
isResSent(res) && !next.wasCalled && next();
I'll need to spend some time setting up something to detect if next was called. Any initial thoughts on that idea?
I would say that the current design choice for this library is not ideal. I don't really find a way to really work this out reliably.
Also consider this case below:
const handler = nc()
.get(async (req, res, next) => {
somePromise().then(() => res.end()); // <- notice this is not returned and not await for
// or setTimeout(() => res.end(), 0);
})
After the handler is resolved. isResSent()
is obviously false
because res.end()
has not been called (it calls in the next tick, or after the promise is resolved, which the function does not wait for). With this the check of isResSent()
then resolve promise will never be triggered.
At this point, it just makes the most sense to not returning a promise in a handler, since we never know when that promise is supposed to be resolved.
The other way to solve this is to follow this pattern https://github.com/hoangvvo/next-connect/pull/148 (similar to Koa or Go-Gin). With this we can for sure know the promise will be resolve (since next() is the call to next function, keep awaiting them and when it resolves, it is the time to do so in the handler). However, this breaks current support with express middleware.
Sorry about the wait, I just have some time to revisit this PR today. I think I finally agree that this issue should be resolved. However, one concern is that sometimes I want to do some post processing after the response is sent. In that case, it would not really make sense to end the promise there. What do you think about that case. Example:
const handler = nc() .get(async (req, res, next) => { res.status(200).send('hello!'); next(); }) .all(async (req, res) => { // it would have been resolved before this step await logPostRequestToDatabase(req); }; export default handler;
No worries! Thanks for spending some time on it. Good point about running functions after the response is sent. My initial thought it to call
next
instead ofdone
if the response is sent, but testing that, I'm seeing next called extra times which causesdone
to be called multiple times. So, do we do something like this?
isResSent(res) && !next.wasCalled && next();
I'll need to spend some time setting up something to detect if next was called. Any initial thoughts on that idea?
Funny enough this is not the first time the issue brought up. https://github.com/senchalabs/connect/issues/1042 (so the same case in Express.js) was here long before and the fact that it is not yet solved indicating this is a problematic issue. It is probably due to the bad design choice of Express.js (therefore, of this library too) that makes this nearly impossible to solve.
I gave an attempt to resolve this in https://github.com/hoangvvo/next-connect/pull/178. Could you check it out whenever you have the time?
My solution is to listen to the event res.on("close")
https://nodejs.org/api/http.html#event-close_1
Thanks for your thoughts on the matter and good points about the design limitations. I had a brief look at your PR, and I'll have time to take a closer look at it tomorrow.
Closing in favor of #178. Thanks for the PR.
This change allows
handler(res, req)
to resolve for middlewares that don't call next, but end the response.Example: