From b35fa26dc120f19db41641fc73b72596e7861306 Mon Sep 17 00:00:00 2001 From: Robin Tail Date: Tue, 16 Jun 2026 18:24:52 +0200 Subject: [PATCH 1/3] br(server): making createServer sync again, incl. ServerHook. --- example/index.ts | 7 +---- express-zod-api/src/config-type.ts | 3 +-- express-zod-api/src/server.ts | 6 ++--- express-zod-api/tests/server.spec.ts | 40 ++++++++++++++-------------- express-zod-api/tests/system.spec.ts | 18 ++++++++----- 5 files changed, 36 insertions(+), 38 deletions(-) diff --git a/example/index.ts b/example/index.ts index 3292f7dfcf..3fedff3630 100644 --- a/example/index.ts +++ b/example/index.ts @@ -2,9 +2,4 @@ import { createServer } from "express-zod-api"; import { config } from "./config.ts"; import { routing } from "./routing.ts"; -/** - * "await" is only needed for using entities returned from this method. - * If you can not use await (on the top level of CJS), use IIFE wrapper: - * @example (async () => { await ... })() - * */ -await createServer(config, routing); +createServer(config, routing); diff --git a/express-zod-api/src/config-type.ts b/express-zod-api/src/config-type.ts index 9ba9c3a349..6f8253df4d 100644 --- a/express-zod-api/src/config-type.ts +++ b/express-zod-api/src/config-type.ts @@ -156,12 +156,11 @@ interface GracefulOptions { beforeExit?: () => void | Promise; } -/** @todo consider removing Promise to make createServer sync in next major */ type ServerHook = (params: { app: IRouter; /** @desc Returns child logger for the given request (if configured) or the configured logger otherwise */ getLogger: GetLogger; -}) => void | Promise; +}) => void; export interface HttpConfig { /** @desc Port, UNIX socket or custom options. */ diff --git a/express-zod-api/src/server.ts b/express-zod-api/src/server.ts index df512e2ee3..01a63019e0 100644 --- a/express-zod-api/src/server.ts +++ b/express-zod-api/src/server.ts @@ -63,7 +63,7 @@ export const attachRouting = (config: AppConfig, routing: Routing) => { return { notFoundHandler, logger }; }; -export const createServer = async (config: ServerConfig, routing: Routing) => { +export const createServer = (config: ServerConfig, routing: Routing) => { const { logger, getLogger, notFoundHandler, catcher, loggingMiddleware } = makeCommonEntities(config); const app = express() @@ -80,7 +80,7 @@ export const createServer = async (config: ServerConfig, routing: Routing) => { ); } if (config.cookies) app.use(createCookieParser({ config })); - await config.beforeRouting?.({ app, getLogger }); + config.beforeRouting?.({ app, getLogger }); const parsers: Parsers = { json: [config.jsonParser || express.json()], @@ -90,7 +90,7 @@ export const createServer = async (config: ServerConfig, routing: Routing) => { }; initRouting({ app, routing, getLogger, config, parsers }); - await config.afterRouting?.({ app, getLogger }); + config.afterRouting?.({ app, getLogger }); app.use(catcher, notFoundHandler); const created: Array = []; diff --git a/express-zod-api/tests/server.spec.ts b/express-zod-api/tests/server.spec.ts index adae388cc9..91acfd005c 100644 --- a/express-zod-api/tests/server.spec.ts +++ b/express-zod-api/tests/server.spec.ts @@ -40,7 +40,7 @@ describe("Server", () => { }); describe("createServer()", () => { - test("Should create server with minimal config", async () => { + test("Should create server with minimal config", () => { const port = givePort(); const configMock = { http: { listen: port }, @@ -58,7 +58,7 @@ describe("Server", () => { }), }, }; - const { servers } = await createServer(configMock, routingMock); + const { servers } = createServer(configMock, routingMock); expect(servers).toHaveLength(1); expect(servers[0]).toBeTruthy(); expect(appMock).toBeTruthy(); @@ -90,7 +90,7 @@ describe("Server", () => { expect(httpListenSpy).toHaveBeenCalledWith(port, expect.any(Function)); }); - test("Should create server with custom parsers, logger, error handler and hooks", async () => { + test("Should create server with custom parsers, logger, error handler and hooks", () => { const customLogger = new BuiltinLogger({ level: "silent" }); const infoMethod = vi.spyOn(customLogger, "info"); const port = givePort(); @@ -135,7 +135,7 @@ describe("Server", () => { }), }, }; - const { logger, app } = await createServer( + const { logger, app } = createServer( configMock as unknown as ServerConfig, routingMock, ); @@ -211,7 +211,7 @@ describe("Server", () => { ); }); - test("should create a HTTPS server on request", async () => { + test("should create a HTTPS server on request", () => { const configMock = { https: { listen: givePort(), @@ -230,7 +230,7 @@ describe("Server", () => { }, }; - const { servers } = await createServer(configMock, routingMock); + const { servers } = createServer(configMock, routingMock); expect(servers).toHaveLength(1); expect(servers[0]).toBeTruthy(); expect(createHttpsServerSpy).toHaveBeenCalledWith( @@ -244,7 +244,7 @@ describe("Server", () => { ); }); - test("should create both HTTP and HTTPS servers", async () => { + test("should create both HTTP and HTTPS servers", () => { const configMock = { http: { listen: givePort() }, https: { @@ -255,23 +255,23 @@ describe("Server", () => { startupLogo: false, logger: { level: "warn" as const }, }; - const { servers } = await createServer(configMock, {}); + const { servers } = createServer(configMock, {}); expect(servers).toHaveLength(2); expect(servers[0]).toBeTruthy(); expect(servers[1]).toBeTruthy(); }); - test("should warn when neigher configured", async () => { + test("should warn when neigher configured", () => { const customLogger = new BuiltinLogger({ level: "silent" }); const warnMethod = vi.spyOn(customLogger, "warn"); - await createServer( + createServer( { cors: false, startupLogo: false, logger: customLogger }, {}, ); expect(warnMethod).toHaveBeenCalledWith("No servers configured."); }); - test("should enable compression on request", async () => { + test("should enable compression on request", () => { const configMock = { http: { listen: givePort() }, compression: true, @@ -279,7 +279,7 @@ describe("Server", () => { startupLogo: false, logger: { level: "warn" }, } satisfies ServerConfig; - await createServer(configMock, {}); + createServer(configMock, {}); expect(appMock.use).toHaveBeenCalledTimes(3); expect(compressionMock).toHaveBeenCalledTimes(1); expect(compressionMock).toHaveBeenCalledWith(undefined); @@ -287,7 +287,7 @@ describe("Server", () => { test.each([true, { secret: "my-secret" }])( "should enable cookie parser on demand %#", - async (cookies) => { + (cookies) => { const configMock = { http: { listen: givePort() }, cookies, @@ -295,7 +295,7 @@ describe("Server", () => { startupLogo: false, logger: { level: "warn" }, } satisfies ServerConfig; - await createServer(configMock, {}); + createServer(configMock, {}); expect(appMock.use).toHaveBeenCalledTimes(3); expect(cookieParserMock).toHaveBeenCalledTimes(1); expect(cookieParserMock).toHaveBeenCalledWith( @@ -305,7 +305,7 @@ describe("Server", () => { }, ); - test("should enable uploads on request", async () => { + test("should enable uploads on request", () => { const configMock = { http: { listen: givePort() }, upload: { @@ -328,7 +328,7 @@ describe("Server", () => { }), }, }; - await createServer(configMock, routingMock); + createServer(configMock, routingMock); expect(appMock.use).toHaveBeenCalledTimes(2); expect(appMock.get).toHaveBeenCalledTimes(1); expect(appMock.get).toHaveBeenCalledWith( @@ -340,7 +340,7 @@ describe("Server", () => { ); }); - test("should enable raw on request", async () => { + test("should enable raw on request", () => { const configMock = { http: { listen: givePort() }, cors: true, @@ -356,7 +356,7 @@ describe("Server", () => { }), }, }; - await createServer(configMock, routingMock); + createServer(configMock, routingMock); expect(appMock.use).toHaveBeenCalledTimes(2); expect(appMock.get).toHaveBeenCalledTimes(1); expect(appMock.get).toHaveBeenCalledWith( @@ -368,7 +368,7 @@ describe("Server", () => { ); }); - test("should enable urlencoded on request", async () => { + test("should enable urlencoded on request", () => { const configMock = { http: { listen: givePort() }, cors: false, @@ -383,7 +383,7 @@ describe("Server", () => { }), }, }; - await createServer(configMock, routingMock); + createServer(configMock, routingMock); expect(appMock.use).toHaveBeenCalledTimes(2); expect(appMock.get).toHaveBeenCalledTimes(1); expect(appMock.get).toHaveBeenCalledWith( diff --git a/express-zod-api/tests/system.spec.ts b/express-zod-api/tests/system.spec.ts index 294a6d88d1..7355c57cef 100644 --- a/express-zod-api/tests/system.spec.ts +++ b/express-zod-api/tests/system.spec.ts @@ -16,8 +16,9 @@ import { } from "../src"; import { givePort } from "../../tools/ports"; import { setTimeout } from "node:timers/promises"; +import { beforeAll } from "vitest"; -describe("App in production mode", async () => { +describe("App in production mode", () => { vi.stubEnv("TSDOWN_STATIC", "production"); vi.stubEnv("NODE_ENV", "production"); const port = givePort(); @@ -176,13 +177,16 @@ describe("App in production mode", async () => { }); const { servers: [server], - } = await createServer(config, routing); + } = createServer(config, routing); expect(server).toBeTruthy(); - await vi.waitFor(() => assert(server!.listening), { timeout: 1e4 }); - expect(warnMethod).toHaveBeenCalledWith( - "DeprecationError (express): Sample deprecation message", - expect.any(Array), // stack - ); + + beforeAll(async () => { + await vi.waitFor(() => assert(server!.listening), { timeout: 1e4 }); + expect(warnMethod).toHaveBeenCalledWith( + "DeprecationError (express): Sample deprecation message", + expect.any(Array), // stack + ); + }); afterAll(async () => { server!.close(); From a47fcc152725cfe925d1a1a60c779e1b3f7abcfa Mon Sep 17 00:00:00 2001 From: Robin Tail Date: Tue, 16 Jun 2026 18:26:06 +0200 Subject: [PATCH 2/3] Readme: updating example. --- README.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/README.md b/README.md index 23da8d5afc..2105d5111f 100644 --- a/README.md +++ b/README.md @@ -683,9 +683,7 @@ const config = createConfig({ }, // ... cors, logger, etc }); -// 'await' is only needed if you're going to use the returned entities. -// For top level CJS you can wrap you code with (async () => { ... })() -const { app, servers, logger } = await createServer(config, routing); +const { app, servers, logger } = createServer(config, routing); ``` Ensure having `@types/node` package installed. At least you need to specify the port (usually it is 443) or UNIX socket, From 3c949aca6a85cfe48d734ef94db8caccc8d2098f Mon Sep 17 00:00:00 2001 From: Robin Tail Date: Tue, 16 Jun 2026 18:38:55 +0200 Subject: [PATCH 3/3] fix(test): rm redundant import. --- express-zod-api/tests/system.spec.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/express-zod-api/tests/system.spec.ts b/express-zod-api/tests/system.spec.ts index 7355c57cef..2fa76248e7 100644 --- a/express-zod-api/tests/system.spec.ts +++ b/express-zod-api/tests/system.spec.ts @@ -16,7 +16,6 @@ import { } from "../src"; import { givePort } from "../../tools/ports"; import { setTimeout } from "node:timers/promises"; -import { beforeAll } from "vitest"; describe("App in production mode", () => { vi.stubEnv("TSDOWN_STATIC", "production");