From ba0051c73892ccc073709dcb78e75b1a823dfd78 Mon Sep 17 00:00:00 2001 From: Anna Bocharova Date: Sun, 21 Sep 2025 17:16:12 +0200 Subject: [PATCH 1/7] Add eslint rule. --- eslint.config.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/eslint.config.js b/eslint.config.js index 6611ac4be..09108604a 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -33,6 +33,13 @@ const importConcerns = [ })), ]; +const compatibilityConerns = [ + { + selector: "CallExpression > MemberExpression[property.name='example']", + message: "avoid using example() method to operate without zod plugin", + }, +]; + const performanceConcerns = [ { selector: "ImportDeclaration[source.value=/assert/]", // #2169 @@ -210,6 +217,7 @@ export default tsPlugin.config( "warn", ...importConcerns, ...performanceConcerns, + ...compatibilityConerns, ], }, }, From 1f02414019cf7c123b80c112d18b8b2ba1e33598 Mon Sep 17 00:00:00 2001 From: Anna Bocharova Date: Sun, 21 Sep 2025 17:23:34 +0200 Subject: [PATCH 2/7] Using globalRegistry.add() instead of .example() method in framework sources. --- express-zod-api/src/result-handler.ts | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/express-zod-api/src/result-handler.ts b/express-zod-api/src/result-handler.ts index 4c7038f76..ef3166416 100644 --- a/express-zod-api/src/result-handler.ts +++ b/express-zod-api/src/result-handler.ts @@ -112,15 +112,17 @@ export const defaultResultHandler = new ResultHandler({ } return responseSchema; }, - negative: z - .object({ + negative: () => { + const schema = z.object({ status: z.literal("error"), error: z.object({ message: z.string() }), - }) - .example({ - status: "error", - error: { message: "Sample error message" }, - }), + }); + const examples: z.output[] = [ + { status: "error", error: { message: "Sample error message" } }, + ]; + globalRegistry.add(schema, { examples }); + return schema; + }, handler: ({ error, input, output, request, response, logger }) => { if (error) { const httpError = ensureHttpError(error); @@ -169,9 +171,11 @@ export const arrayResultHandler = new ResultHandler({ } return responseSchema; }, - negative: { - schema: z.string().example("Sample error message"), - mimeType: "text/plain", + negative: () => { + const schema = z.string(); + const examples: z.output[] = ["Sample error message"]; + globalRegistry.add(schema, { examples }); + return { schema, mimeType: "text/plain" }; }, handler: ({ response, output, error, logger, request, input }) => { if (error) { From 22d9a45eab39c815f85afc86c501c4cce316490c Mon Sep 17 00:00:00 2001 From: Anna Bocharova Date: Sun, 21 Sep 2025 17:32:56 +0200 Subject: [PATCH 3/7] Changelog: 24.7.4. --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index fcfd8d545..31a5087ee 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,13 @@ ## Version 24 +### v24.7.4 + +- This patch fixes the issue for users facing `TypeError: .example is not a function`, but not using that method: + - It was reported that in some environments Zod plugin is not loaded correctly so that the usage of + `ZodType::example()` (plugin method) internally by the framework itself was causing this issue; + - This version fixes the problem by replacing the usage of `.example()` with `globalRegistry.add()`. + ### v24.7.3 - Fixed the depiction of the negative response to `HEAD` requests: From c50e58f3d7838e3ed6de5b23a25502c0460898e3 Mon Sep 17 00:00:00 2001 From: Anna Bocharova Date: Sun, 21 Sep 2025 17:41:33 +0200 Subject: [PATCH 4/7] Avoid memory leak by defining the negative schema statically. --- express-zod-api/src/result-handler.ts | 36 ++++++++++++++------------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/express-zod-api/src/result-handler.ts b/express-zod-api/src/result-handler.ts index ef3166416..286d784ec 100644 --- a/express-zod-api/src/result-handler.ts +++ b/express-zod-api/src/result-handler.ts @@ -95,6 +95,16 @@ export class ResultHandler< } } +const defaultNegativeSchema = z.object({ + status: z.literal("error"), + error: z.object({ message: z.string() }), +}); +globalRegistry.add(defaultNegativeSchema, { + examples: [ + { status: "error", error: { message: "Sample error message" } }, + ] satisfies z.output[], +}); + export const defaultResultHandler = new ResultHandler({ positive: (output) => { const responseSchema = z.object({ @@ -112,17 +122,7 @@ export const defaultResultHandler = new ResultHandler({ } return responseSchema; }, - negative: () => { - const schema = z.object({ - status: z.literal("error"), - error: z.object({ message: z.string() }), - }); - const examples: z.output[] = [ - { status: "error", error: { message: "Sample error message" } }, - ]; - globalRegistry.add(schema, { examples }); - return schema; - }, + negative: defaultNegativeSchema, handler: ({ error, input, output, request, response, logger }) => { if (error) { const httpError = ensureHttpError(error); @@ -141,6 +141,13 @@ export const defaultResultHandler = new ResultHandler({ }, }); +const arrayNegativeSchema = z.string(); +globalRegistry.add(arrayNegativeSchema, { + examples: ["Sample error message"] satisfies z.output< + typeof arrayNegativeSchema + >[], +}); + /** * @deprecated Resist the urge of using it: this handler is designed only to simplify the migration of legacy APIs. * @desc Responding with array is a bad practice keeping your endpoints from evolving without breaking changes. @@ -171,12 +178,7 @@ export const arrayResultHandler = new ResultHandler({ } return responseSchema; }, - negative: () => { - const schema = z.string(); - const examples: z.output[] = ["Sample error message"]; - globalRegistry.add(schema, { examples }); - return { schema, mimeType: "text/plain" }; - }, + negative: { schema: arrayNegativeSchema, mimeType: "text/plain" }, handler: ({ response, output, error, logger, request, input }) => { if (error) { const httpError = ensureHttpError(error); From 3f03ac36a6a13d9e500519b906130abee0218dd8 Mon Sep 17 00:00:00 2001 From: Anna Bocharova Date: Sun, 21 Sep 2025 17:50:36 +0200 Subject: [PATCH 5/7] Fix typo. --- eslint.config.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/eslint.config.js b/eslint.config.js index 09108604a..25f637918 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -33,7 +33,7 @@ const importConcerns = [ })), ]; -const compatibilityConerns = [ +const compatibilityConcerns = [ { selector: "CallExpression > MemberExpression[property.name='example']", message: "avoid using example() method to operate without zod plugin", @@ -217,7 +217,7 @@ export default tsPlugin.config( "warn", ...importConcerns, ...performanceConcerns, - ...compatibilityConerns, + ...compatibilityConcerns, ], }, }, From 11d70ae9c0a6a9115755170ed743f4c9039ff0dc Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 21 Sep 2025 15:52:43 +0000 Subject: [PATCH 6/7] express-zod-api version 24.7.4-beta.1 --- express-zod-api/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/express-zod-api/package.json b/express-zod-api/package.json index 907dabdb9..5568cdbfb 100644 --- a/express-zod-api/package.json +++ b/express-zod-api/package.json @@ -1,6 +1,6 @@ { "name": "express-zod-api", - "version": "24.7.3", + "version": "24.7.4-beta.1", "description": "A Typescript framework to help you get an API server up and running with I/O schema validation and custom middlewares in minutes.", "license": "MIT", "repository": { From 122157b453f11ed53b1efedeb4e424e01390f5e7 Mon Sep 17 00:00:00 2001 From: Anna Bocharova Date: Sun, 21 Sep 2025 17:55:28 +0200 Subject: [PATCH 7/7] Contributor attribution. --- CHANGELOG.md | 7 ++++--- README.md | 1 + 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 31a5087ee..20ac9a9fc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,9 +5,10 @@ ### v24.7.4 - This patch fixes the issue for users facing `TypeError: .example is not a function`, but not using that method: - - It was reported that in some environments Zod plugin is not loaded correctly so that the usage of - `ZodType::example()` (plugin method) internally by the framework itself was causing this issue; - - This version fixes the problem by replacing the usage of `.example()` with `globalRegistry.add()`. + - Some environments fail to load Zod plugin properly so that the usage of `ZodType::example()` (plugin method) + internally by the framework itself could cause that error; + - This version fixes the problem by replacing the usage of `.example()` with `globalRegistry.add()`; + - The issue was found and reported by [@misha-z1nchuk](https://github.com/misha-z1nchuk). ### v24.7.3 diff --git a/README.md b/README.md index 79da6c8a2..f3aa87647 100644 --- a/README.md +++ b/README.md @@ -85,6 +85,7 @@ Therefore, many basic tasks can be accomplished faster and easier, in particular These people contributed to the improvement of the framework by reporting bugs, making changes and suggesting ideas: +[@misha-z1nchuk](https://github.com/misha-z1nchuk) [@pepegc](https://github.com/pepegc) [@MichaelHindley](https://github.com/MichaelHindley) [@zoton2](https://github.com/zoton2)