Skip to content

Commit 66d585d

Browse files
authored
br(v9): Making createServer() sync again (#3472)
Thanks to #3465 This should simplify daily routines for beginners. ## Tradeoffs - Removed `Promise` from `ServerHook` type => `beforeRouting` and `afterRouting` become sync - No further async features to `createServer` <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Documentation** * Updated code examples to show synchronous server initialization without `await` * **Refactor** * Server creation function is now fully synchronous; remove `await` when invoking it * Server lifecycle hooks execute synchronously during initialization <!-- end of auto-generated comment: release notes by coderabbit.ai -->
1 parent 81cd7e3 commit 66d585d

6 files changed

Lines changed: 36 additions & 41 deletions

File tree

README.md

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -683,9 +683,7 @@ const config = createConfig({
683683
}, // ... cors, logger, etc
684684
});
685685

686-
// 'await' is only needed if you're going to use the returned entities.
687-
// For top level CJS you can wrap you code with (async () => { ... })()
688-
const { app, servers, logger } = await createServer(config, routing);
686+
const { app, servers, logger } = createServer(config, routing);
689687
```
690688

691689
Ensure having `@types/node` package installed. At least you need to specify the port (usually it is 443) or UNIX socket,

example/index.ts

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,4 @@ import { createServer } from "express-zod-api";
22
import { config } from "./config.ts";
33
import { routing } from "./routing.ts";
44

5-
/**
6-
* "await" is only needed for using entities returned from this method.
7-
* If you can not use await (on the top level of CJS), use IIFE wrapper:
8-
* @example (async () => { await ... })()
9-
* */
10-
await createServer(config, routing);
5+
createServer(config, routing);

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

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -156,12 +156,11 @@ interface GracefulOptions {
156156
beforeExit?: () => void | Promise<void>;
157157
}
158158

159-
/** @todo consider removing Promise to make createServer sync in next major */
160159
type ServerHook = (params: {
161160
app: IRouter;
162161
/** @desc Returns child logger for the given request (if configured) or the configured logger otherwise */
163162
getLogger: GetLogger;
164-
}) => void | Promise<void>;
163+
}) => void;
165164

166165
export interface HttpConfig {
167166
/** @desc Port, UNIX socket or custom options. */

express-zod-api/src/server.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ export const attachRouting = (config: AppConfig, routing: Routing) => {
6363
return { notFoundHandler, logger };
6464
};
6565

66-
export const createServer = async (config: ServerConfig, routing: Routing) => {
66+
export const createServer = (config: ServerConfig, routing: Routing) => {
6767
const { logger, getLogger, notFoundHandler, catcher, loggingMiddleware } =
6868
makeCommonEntities(config);
6969
const app = express()
@@ -80,7 +80,7 @@ export const createServer = async (config: ServerConfig, routing: Routing) => {
8080
);
8181
}
8282
if (config.cookies) app.use(createCookieParser({ config }));
83-
await config.beforeRouting?.({ app, getLogger });
83+
config.beforeRouting?.({ app, getLogger });
8484

8585
const parsers: Parsers = {
8686
json: [config.jsonParser || express.json()],
@@ -90,7 +90,7 @@ export const createServer = async (config: ServerConfig, routing: Routing) => {
9090
};
9191
initRouting({ app, routing, getLogger, config, parsers });
9292

93-
await config.afterRouting?.({ app, getLogger });
93+
config.afterRouting?.({ app, getLogger });
9494
app.use(catcher, notFoundHandler);
9595

9696
const created: Array<http.Server | https.Server> = [];

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

Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ describe("Server", () => {
4040
});
4141

4242
describe("createServer()", () => {
43-
test("Should create server with minimal config", async () => {
43+
test("Should create server with minimal config", () => {
4444
const port = givePort();
4545
const configMock = {
4646
http: { listen: port },
@@ -58,7 +58,7 @@ describe("Server", () => {
5858
}),
5959
},
6060
};
61-
const { servers } = await createServer(configMock, routingMock);
61+
const { servers } = createServer(configMock, routingMock);
6262
expect(servers).toHaveLength(1);
6363
expect(servers[0]).toBeTruthy();
6464
expect(appMock).toBeTruthy();
@@ -90,7 +90,7 @@ describe("Server", () => {
9090
expect(httpListenSpy).toHaveBeenCalledWith(port, expect.any(Function));
9191
});
9292

93-
test("Should create server with custom parsers, logger, error handler and hooks", async () => {
93+
test("Should create server with custom parsers, logger, error handler and hooks", () => {
9494
const customLogger = new BuiltinLogger({ level: "silent" });
9595
const infoMethod = vi.spyOn(customLogger, "info");
9696
const port = givePort();
@@ -135,7 +135,7 @@ describe("Server", () => {
135135
}),
136136
},
137137
};
138-
const { logger, app } = await createServer(
138+
const { logger, app } = createServer(
139139
configMock as unknown as ServerConfig,
140140
routingMock,
141141
);
@@ -211,7 +211,7 @@ describe("Server", () => {
211211
);
212212
});
213213

214-
test("should create a HTTPS server on request", async () => {
214+
test("should create a HTTPS server on request", () => {
215215
const configMock = {
216216
https: {
217217
listen: givePort(),
@@ -230,7 +230,7 @@ describe("Server", () => {
230230
},
231231
};
232232

233-
const { servers } = await createServer(configMock, routingMock);
233+
const { servers } = createServer(configMock, routingMock);
234234
expect(servers).toHaveLength(1);
235235
expect(servers[0]).toBeTruthy();
236236
expect(createHttpsServerSpy).toHaveBeenCalledWith(
@@ -244,7 +244,7 @@ describe("Server", () => {
244244
);
245245
});
246246

247-
test("should create both HTTP and HTTPS servers", async () => {
247+
test("should create both HTTP and HTTPS servers", () => {
248248
const configMock = {
249249
http: { listen: givePort() },
250250
https: {
@@ -255,47 +255,47 @@ describe("Server", () => {
255255
startupLogo: false,
256256
logger: { level: "warn" as const },
257257
};
258-
const { servers } = await createServer(configMock, {});
258+
const { servers } = createServer(configMock, {});
259259
expect(servers).toHaveLength(2);
260260
expect(servers[0]).toBeTruthy();
261261
expect(servers[1]).toBeTruthy();
262262
});
263263

264-
test("should warn when neigher configured", async () => {
264+
test("should warn when neigher configured", () => {
265265
const customLogger = new BuiltinLogger({ level: "silent" });
266266
const warnMethod = vi.spyOn(customLogger, "warn");
267-
await createServer(
267+
createServer(
268268
{ cors: false, startupLogo: false, logger: customLogger },
269269
{},
270270
);
271271
expect(warnMethod).toHaveBeenCalledWith("No servers configured.");
272272
});
273273

274-
test("should enable compression on request", async () => {
274+
test("should enable compression on request", () => {
275275
const configMock = {
276276
http: { listen: givePort() },
277277
compression: true,
278278
cors: true,
279279
startupLogo: false,
280280
logger: { level: "warn" },
281281
} satisfies ServerConfig;
282-
await createServer(configMock, {});
282+
createServer(configMock, {});
283283
expect(appMock.use).toHaveBeenCalledTimes(3);
284284
expect(compressionMock).toHaveBeenCalledTimes(1);
285285
expect(compressionMock).toHaveBeenCalledWith(undefined);
286286
});
287287

288288
test.each([true, { secret: "my-secret" }])(
289289
"should enable cookie parser on demand %#",
290-
async (cookies) => {
290+
(cookies) => {
291291
const configMock = {
292292
http: { listen: givePort() },
293293
cookies,
294294
cors: true,
295295
startupLogo: false,
296296
logger: { level: "warn" },
297297
} satisfies ServerConfig;
298-
await createServer(configMock, {});
298+
createServer(configMock, {});
299299
expect(appMock.use).toHaveBeenCalledTimes(3);
300300
expect(cookieParserMock).toHaveBeenCalledTimes(1);
301301
expect(cookieParserMock).toHaveBeenCalledWith(
@@ -305,7 +305,7 @@ describe("Server", () => {
305305
},
306306
);
307307

308-
test("should enable uploads on request", async () => {
308+
test("should enable uploads on request", () => {
309309
const configMock = {
310310
http: { listen: givePort() },
311311
upload: {
@@ -328,7 +328,7 @@ describe("Server", () => {
328328
}),
329329
},
330330
};
331-
await createServer(configMock, routingMock);
331+
createServer(configMock, routingMock);
332332
expect(appMock.use).toHaveBeenCalledTimes(2);
333333
expect(appMock.get).toHaveBeenCalledTimes(1);
334334
expect(appMock.get).toHaveBeenCalledWith(
@@ -340,7 +340,7 @@ describe("Server", () => {
340340
);
341341
});
342342

343-
test("should enable raw on request", async () => {
343+
test("should enable raw on request", () => {
344344
const configMock = {
345345
http: { listen: givePort() },
346346
cors: true,
@@ -356,7 +356,7 @@ describe("Server", () => {
356356
}),
357357
},
358358
};
359-
await createServer(configMock, routingMock);
359+
createServer(configMock, routingMock);
360360
expect(appMock.use).toHaveBeenCalledTimes(2);
361361
expect(appMock.get).toHaveBeenCalledTimes(1);
362362
expect(appMock.get).toHaveBeenCalledWith(
@@ -368,7 +368,7 @@ describe("Server", () => {
368368
);
369369
});
370370

371-
test("should enable urlencoded on request", async () => {
371+
test("should enable urlencoded on request", () => {
372372
const configMock = {
373373
http: { listen: givePort() },
374374
cors: false,
@@ -383,7 +383,7 @@ describe("Server", () => {
383383
}),
384384
},
385385
};
386-
await createServer(configMock, routingMock);
386+
createServer(configMock, routingMock);
387387
expect(appMock.use).toHaveBeenCalledTimes(2);
388388
expect(appMock.get).toHaveBeenCalledTimes(1);
389389
expect(appMock.get).toHaveBeenCalledWith(

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

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import {
1717
import { givePort } from "../../tools/ports";
1818
import { setTimeout } from "node:timers/promises";
1919

20-
describe("App in production mode", async () => {
20+
describe("App in production mode", () => {
2121
vi.stubEnv("TSDOWN_STATIC", "production");
2222
vi.stubEnv("NODE_ENV", "production");
2323
const port = givePort();
@@ -176,13 +176,16 @@ describe("App in production mode", async () => {
176176
});
177177
const {
178178
servers: [server],
179-
} = await createServer(config, routing);
179+
} = createServer(config, routing);
180180
expect(server).toBeTruthy();
181-
await vi.waitFor(() => assert(server!.listening), { timeout: 1e4 });
182-
expect(warnMethod).toHaveBeenCalledWith(
183-
"DeprecationError (express): Sample deprecation message",
184-
expect.any(Array), // stack
185-
);
181+
182+
beforeAll(async () => {
183+
await vi.waitFor(() => assert(server!.listening), { timeout: 1e4 });
184+
expect(warnMethod).toHaveBeenCalledWith(
185+
"DeprecationError (express): Sample deprecation message",
186+
expect.any(Array), // stack
187+
);
188+
});
186189

187190
afterAll(async () => {
188191
server!.close();

0 commit comments

Comments
 (0)