Skip to content

Commit 403404f

Browse files
authored
Port for v25: avoid using example method in sources (#2955)
Cherry-picked 3aacc05 , #2954 <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - New Features - Standardized error handling with reusable default and array error schemas, including documented examples. - arrayResultHandler now uses a structured negative configuration (schema + mimeType). - Bug Fixes - Resolved a TypeError occurring in some environments related to generating examples. - Documentation - Added v25.4.1 changelog entry with details and attribution. - Updated README with additional contributor avatars. - Chores - Introduced an ESLint rule to flag incompatible example() usage, improving cross-environment compatibility. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
1 parent e9dbae6 commit 403404f

File tree

4 files changed

+36
-13
lines changed

4 files changed

+36
-13
lines changed

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,14 @@
22

33
## Version 25
44

5+
### v25.4.1
6+
7+
- This patch fixes the issue for users facing `TypeError: .example is not a function`, but not using that method:
8+
- Some environments fail to load Zod plugin properly so that the usage of `ZodType::example()` (plugin method)
9+
internally by the framework itself could cause that error;
10+
- This version fixes the problem by replacing the usage of `.example()` with `globalRegistry.add()`;
11+
- The issue was found and reported by [@misha-z1nchuk](https://github.com/misha-z1nchuk).
12+
513
### v25.4.0
614

715
- Feat: configurable query parser:

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ Therefore, many basic tasks can be accomplished faster and easier, in particular
8585

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

88+
[<img src="https://github.com/misha-z1nchuk.png" alt="@misha-z1nchuk" width="50px" />](https://github.com/misha-z1nchuk)
8889
[<img src="https://github.com/GreaterTamarack.png" alt="@GreaterTamarack" width="50px" />](https://github.com/GreaterTamarack)
8990
[<img src="https://github.com/pepegc.png" alt="@pepegc" width="50px" />](https://github.com/pepegc)
9091
[<img src="https://github.com/MichaelHindley.png" alt="@MichaelHindley" width="50px" />](https://github.com/MichaelHindley)

eslint.config.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,13 @@ const importConcerns = [
3030
})),
3131
];
3232

33+
const compatibilityConcerns = [
34+
{
35+
selector: "CallExpression > MemberExpression[property.name='example']",
36+
message: "avoid using example() method to operate without zod plugin",
37+
},
38+
];
39+
3340
const performanceConcerns = [
3441
{
3542
selector: "ImportDeclaration[source.value=/assert/]", // #2169
@@ -202,6 +209,7 @@ export default tsPlugin.config(
202209
"warn",
203210
...importConcerns,
204211
...performanceConcerns,
212+
...compatibilityConcerns,
205213
],
206214
},
207215
},

express-zod-api/src/result-handler.ts

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,16 @@ export class ResultHandler<
9494
}
9595
}
9696

97+
const defaultNegativeSchema = z.object({
98+
status: z.literal("error"),
99+
error: z.object({ message: z.string() }),
100+
});
101+
globalRegistry.add(defaultNegativeSchema, {
102+
examples: [
103+
{ status: "error", error: { message: "Sample error message" } },
104+
] satisfies z.output<typeof defaultNegativeSchema>[],
105+
});
106+
97107
export const defaultResultHandler = new ResultHandler({
98108
positive: (output) => {
99109
const responseSchema = z.object({
@@ -111,15 +121,7 @@ export const defaultResultHandler = new ResultHandler({
111121
}
112122
return responseSchema;
113123
},
114-
negative: z
115-
.object({
116-
status: z.literal("error"),
117-
error: z.object({ message: z.string() }),
118-
})
119-
.example({
120-
status: "error",
121-
error: { message: "Sample error message" },
122-
}),
124+
negative: defaultNegativeSchema,
123125
handler: ({ error, input, output, request, response, logger }) => {
124126
if (error) {
125127
const httpError = ensureHttpError(error);
@@ -138,6 +140,13 @@ export const defaultResultHandler = new ResultHandler({
138140
},
139141
});
140142

143+
const arrayNegativeSchema = z.string();
144+
globalRegistry.add(arrayNegativeSchema, {
145+
examples: ["Sample error message"] satisfies z.output<
146+
typeof arrayNegativeSchema
147+
>[],
148+
});
149+
141150
/**
142151
* @deprecated Resist the urge of using it: this handler is designed only to simplify the migration of legacy APIs.
143152
* @desc Responding with array is a bad practice keeping your endpoints from evolving without breaking changes.
@@ -170,10 +179,7 @@ export const arrayResultHandler = new ResultHandler({
170179
}
171180
return responseSchema;
172181
},
173-
negative: {
174-
schema: z.string().example("Sample error message"),
175-
mimeType: "text/plain",
176-
},
182+
negative: { schema: arrayNegativeSchema, mimeType: "text/plain" },
177183
handler: ({ response, output, error, logger, request, input }) => {
178184
if (error) {
179185
const httpError = ensureHttpError(error);

0 commit comments

Comments
 (0)