-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
add Express.js forking recipe; rework
onFork
hook to allow forking …
…services to run the forked context inside a callback, thus enabling services to use AsyncLocalStorage
- Loading branch information
Showing
10 changed files
with
142 additions
and
30 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
# Using `container.fork()` with Express | ||
|
||
The sadly ubiquitous Express HTTP server doesn't mesh well with async code. | ||
This means some care must be taken when integrating DICC into an Express-based | ||
application, otherwise container forking and local services won't work as | ||
expected. | ||
|
||
## TL;DR - how do I use DICC with Express? | ||
|
||
Just use the following code snippet as your very first middleware: | ||
|
||
```typescript | ||
app.use((req, res, next) => { | ||
container.fork(async () => { | ||
next(); | ||
|
||
if (!res.closed) { | ||
await new Promise((resolve) => { | ||
res.on('close', resolve); | ||
}); | ||
} | ||
}); | ||
}); | ||
``` | ||
|
||
## Why? | ||
|
||
The problem is that the return value of Express middlewares is completely | ||
ignored. This means that any middleware which is `async` will not be properly | ||
awaited, and so calling `next()` in a middleware will return as soon as all | ||
subsequent middlewares run their _synchronous_ code - but tracking the end of | ||
any _asynchronous_ execution spawned from middlewares is impossible from the | ||
point of a preceding middleware. The problem is illustrated by the following | ||
snippet: | ||
|
||
```typescript | ||
import express from 'express'; | ||
|
||
const app = express(); | ||
|
||
app.use(async (req, res, next) => { | ||
console.log('mw 1 start'); | ||
await next(); | ||
console.log('mw 1 end'); | ||
}); | ||
|
||
app.use(async (req, res, next) => { | ||
console.log('mw 2 start'); | ||
await next(); | ||
console.log('mw 2 end'); | ||
}); | ||
|
||
app.use(async (req, res, next) => { | ||
console.log('mw 3 start'); | ||
await new Promise((r) => setTimeout(r, 250)); | ||
res.end('hello world'); | ||
console.log('mw 3 end'); | ||
}); | ||
|
||
app.listen(8000); | ||
``` | ||
|
||
The script will output the following sequence when a request is handled: | ||
|
||
``` | ||
mw 1 start | ||
mw 2 start | ||
mw 3 start | ||
mw 2 end | ||
mw 1 end | ||
mw 3 end | ||
``` | ||
|
||
Notice the first and second middlewares log the end of their execution _before_ | ||
the last middleware finishes executing, even though each middleware awaits the | ||
`next()` call. This means that `mw 1` has no direct way of telling when `mw 3` | ||
(or any other middleware) finished handling the request. If we were to naively | ||
use something like `container.fork(next)` in `mw 1`, the forked context would | ||
only be available during the synchronous part of the subsequent middlewares, | ||
because the `fork()` method awaits the provided callback and cleans up the | ||
forked context when the callback resolves - but as we've seen, `next()` doesn't | ||
return a Promise, so it will resolve immediately when all synchronous code has | ||
been executed. | ||
|
||
The snippet at the beginning of this recipe works by waiting for the response | ||
stream to be closed before returning from the fork callback. Unless the app | ||
crashes catastrophically, this will ensure that the forked DI context will stay | ||
alive for the entire duration of the request handling pipeline. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters