Skip to content

Commit 1ccffa0

Browse files
authored
Merge branch 'master' into make-v24
2 parents 18d48be + 9ef7d21 commit 1ccffa0

5 files changed

Lines changed: 22 additions & 6 deletions

File tree

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,12 @@ export default [
6666

6767
## Version 23
6868

69+
### v23.6.0
70+
71+
- Featuring `gracefulShutdown.beforeExit()` hook:
72+
- The function to execute after the server was closed, but before terminating the process (can be asynchronous);
73+
- The feature suggested by [@HeikoOsigus](https://github.com/HeikoOsigus).
74+
6975
### v23.5.0
7076

7177
- Integer number `format` in generated Documentation now also depends on the `numericRange` option:

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ Therefore, many basic tasks can be accomplished faster and easier, in particular
8484

8585
These people contributed to the improvement of the framework by reporting bugs, making changes and suggesting ideas:
8686

87+
[<img src="https://github.com/HeikoOsigus.png" alt="@HeikoOsigus" width="50px" />](https://github.com/HeikoOsigus)
8788
[<img src="https://github.com/crgeary.png" alt="@crgeary" width="50px" />](https://github.com/crgeary)
8889
[<img src="https://github.com/williamgcampbell.png" alt="@williamgcampbell" width="50px" />](https://github.com/williamgcampbell)
8990
[<img src="https://github.com/gmorgen1.png" alt="@gmorgen1" width="50px" />](https://github.com/gmorgen1)
@@ -1167,6 +1168,7 @@ createConfig({
11671168
gracefulShutdown: {
11681169
timeout: 1000,
11691170
events: ["SIGINT", "SIGTERM"],
1171+
beforeExit: /* async */ () => {},
11701172
},
11711173
});
11721174
```

express-zod-api/src/config-type.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ type CompressionOptions = Pick<
121121

122122
interface GracefulOptions {
123123
/**
124-
* @desc Time given to drain ongoing requests before exit.
124+
* @desc Time given to drain ongoing requests before closing the server.
125125
* @default 1000
126126
* */
127127
timeout?: number;
@@ -131,6 +131,8 @@ interface GracefulOptions {
131131
* @default [SIGINT, SIGTERM]
132132
* */
133133
events?: string[];
134+
/** @desc The hook to call after the server was closed, but before terminating the process. */
135+
beforeExit?: () => void | Promise<void>;
134136
}
135137

136138
type BeforeRouting = (params: {

express-zod-api/src/server-helpers.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -153,13 +153,17 @@ export const installDeprecationListener = (logger: ActualLogger) =>
153153
export const installTerminationListener = ({
154154
servers,
155155
logger,
156-
options: { timeout, events = ["SIGINT", "SIGTERM"] },
156+
options: { timeout, beforeExit, events = ["SIGINT", "SIGTERM"] },
157157
}: {
158158
servers: Parameters<typeof monitor>[0];
159159
options: Extract<ServerConfig["gracefulShutdown"], object>;
160160
logger: ActualLogger;
161161
}) => {
162162
const graceful = monitor(servers, { logger, timeout });
163-
const onTerm = () => graceful.shutdown().then(() => process.exit());
163+
const onTerm = async () => {
164+
await graceful.shutdown();
165+
await beforeExit?.();
166+
process.exit();
167+
};
164168
for (const trigger of events) process.on(trigger, onTerm);
165169
};

express-zod-api/tests/system.spec.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,7 @@ describe("App in production mode", async () => {
135135
"post /v1/upload": uploadEndpoint,
136136
};
137137
vi.spyOn(process.stdout, "write").mockImplementation(vi.fn()); // mutes logo output
138+
const beforeExit = vi.fn();
138139
const config = createConfig({
139140
http: { listen: port },
140141
compression: { threshold: 1 },
@@ -165,7 +166,7 @@ describe("App in production mode", async () => {
165166
},
166167
cors: false,
167168
startupLogo: true,
168-
gracefulShutdown: { events: ["FAKE"] },
169+
gracefulShutdown: { events: ["FAKE"], beforeExit },
169170
logger,
170171
childLoggerProvider: ({ parent }) =>
171172
Object.defineProperty(parent, "isChild", { value: true }),
@@ -592,7 +593,7 @@ describe("App in production mode", async () => {
592593

593594
describe("Shutdown", () => {
594595
test("should terminate suspended request gracefully on signal", async () => {
595-
const spy = vi
596+
const exitSpy = vi
596597
.spyOn(process, "exit")
597598
.mockImplementation(vi.fn<typeof process.exit>());
598599
fetch(`http://127.0.0.1:${port}/v1/long`).catch((err) =>
@@ -606,7 +607,8 @@ describe("App in production mode", async () => {
606607
});
607608
await setTimeout(1500);
608609
expect(server.listening).toBeFalsy();
609-
expect(spy).toHaveBeenCalled();
610+
expect(beforeExit).toHaveBeenCalledOnce();
611+
expect(exitSpy).toHaveBeenCalled();
610612
});
611613
});
612614
});

0 commit comments

Comments
 (0)