Skip to content

Commit d24c387

Browse files
v25 is for Sara Millerey González (#2741)
### Dedication ![image](https://github.com/user-attachments/assets/47dc18f9-70fa-4276-9c79-2a84ef360642) **Sara Millerey González** was a 32 years young trans woman from Bello, Antioquia, Colombia. She was killed in April 2025. Millerey was raped, tortured, and thrown into a ravine by a group of men. Denounced as a hate crime by press and Antioquia's mayor, the Colombian National Working Group on Violence Based on Victims' Sexual Orientation or Gender Identity is collaborating with the Attorney's General's Office on identifying remaining suspects. Transgender women suffer too frequently from transphobic violence and cruelty, being the less protected social group. I'd like to raise an awareness of this problem. Humans should be creators — not killers. But most importantly, I want every transgender girl to have an opportunity to create applications quickly and, in general, learn to write code easily in order to receive job offers and leave dangerously transphobic territories for more favorable and civilized ones, and live happily there. Protect transgender women. ### Breaking - `zod` version is `^4.0.0`; - Compatibility with `zod@^3` is dropped; - You SHOULD now `import { z } from "zod"` without the `/v4` suffix; - Node.js version is `^20.19.0 || ^22.12.0 || ^24.0.0`; - The framework distribution is now ESM-only (finally); - All the Node.js versions listed above support `require(ESM)` syntax; - Changes to the `Middleware` class: - When the `input` schema is not defined, the `input` argument of the `handler` method is now `unknown`; - Plugin changes to metadata: - `example` removed; - object-based `examples` removed (only array remains); - use either `.example()` or `.meta({})` with `examples` being an array. - Public `getExamples()` helper removed: - use `.meta()?.examples` or `globalRegistry.get()?.examples` instead. ### Content - #2622 - #2711 - #2740 - #2742 - #2777 - #2809 - #2814 - #2815 - #2842 - #2843 - #2844 <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit ## Summary by CodeRabbit * **New Features** * Dropped support for Zod v3; now requires Zod v4 and updates all imports to use `"zod"` directly. * Framework is now distributed as ESM-only and supports Node.js ^20.19.0, ^22.12.0, or ^24.0.0. * Introduced a unified empty schema utility for input/output defaults. * Added Vitest configuration with experimental eventsource flag for testing. * **Breaking Changes** * Removed the public `getExamples()` helper; access examples via `.meta()?.examples` or the global registry. * Middleware and factory input schemas now default to `undefined` when omitted, not an empty object schema. * Dropped support for object-based `examples` in schema metadata; use `.example()` or array-based `examples` instead. * Removed deprecated enum and literal depicters from documentation helpers. * Updated import paths from `"zod/v4"` to `"zod"` throughout codebase and examples. * Adjusted migration rules to reflect import and metadata changes only. * **Bug Fixes** * Improved handling of empty schemas and type inference for middleware and endpoint factories. * **Chores** * Updated documentation and changelogs for new Zod import paths and version requirements. * Updated build, test, and lint configurations for ESM-only output and new dependency versions. * Adjusted GitHub workflows to monitor new branches and updated Node.js versions. * Removed legacy migration rules and simplified migration logic focusing on import and metadata changes. * Removed legacy dependencies like `undici` and adjusted package metadata. * Simplified ESLint and tsup configurations for ESM-only builds. * Updated startup logo dedication message. * **Tests** * Refactored and updated tests to align with new schema defaults and Zod v4 usage. * Simplified migration tests and rules to focus on import path changes. * Added tests for new empty schema utilities. * **Style** * Updated dedication message in the startup logo. * **Revert** * Removed legacy and deprecated migration logic and configuration overrides. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
1 parent 0284b49 commit d24c387

File tree

99 files changed

+599
-957
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

99 files changed

+599
-957
lines changed

.github/workflows/codeql-analysis.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,10 @@ name: "CodeQL"
1313

1414
on:
1515
push:
16-
branches: [ master, v20, v21, v22, v23 ]
16+
branches: [ master, v21, v22, v23, v24 ]
1717
pull_request:
1818
# The branches below must be a subset of the branches above
19-
branches: [ master, v20, v21, v22, v23 ]
19+
branches: [ master, v21, v22, v23, v24 ]
2020
schedule:
2121
- cron: '26 8 * * 1'
2222

.github/workflows/node.js.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,17 @@ name: Node.js CI
55

66
on:
77
push:
8-
branches: [ master, v20, v21, v22, v23 ]
8+
branches: [ master, v21, v22, v23, v24 ]
99
pull_request:
10-
branches: [ master, v20, v21, v22, v23 ]
10+
branches: [ master, v21, v22, v23, v24 ]
1111

1212
jobs:
1313
build:
1414
runs-on: ubuntu-latest
1515
strategy:
1616
fail-fast: false
1717
matrix:
18-
node-version: [20.9.0, 20.x, 22.0.0, 22.x, 24.0.0, 24.x]
18+
node-version: [20.19.0, 20.x, 22.12.0, 22.x, 24.0.0, 24.x]
1919
# See supported Node.js release schedule at https://nodejs.org/en/about/releases/
2020
steps:
2121
- name: Checkout

CHANGELOG.md

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,41 @@
11
# Changelog
22

3+
## Version 25
4+
5+
### v25.0.0
6+
7+
- Supported Node.js versions: `^20.19.0 || ^22.12.0 || ^24.0.0`:
8+
- The framework distribution is now ESM-only (finally);
9+
- All the Node.js versions listed above support `require(ESM)` syntax;
10+
- If facing TypeScript error `TS1479`, ensure either:
11+
- using the [recommended tsconfig base for Node 20+](https://github.com/tsconfig/bases/blob/main/bases/node20.json);
12+
- or switching your project to ESM by setting `"type": "module"` in `package.json`;
13+
- Supported `zod` version: `^4.0.0`;
14+
- Compatibility with `zod@^3` is dropped;
15+
- You SHOULD now `import { z } from "zod"` without the `/v4` suffix;
16+
- Changes to the Zod plugin and metadata processing:
17+
- Dropped support of examples that are given as `example` property of `.meta()` argument;
18+
- Dropped support of examples given within an object-based value of `examples` property of `.meta()` argument;
19+
- Use either `.example()` method or `.meta()` method with `examples` property being an array;
20+
- Changes to the `Middleware` class:
21+
- When the `input` schema is not defined, the `input` argument of the `handler` method is now `unknown`;
22+
- Changes to publicly exposed method:
23+
- The `getExamples()` helper is removed, use `.meta().examples` or `globalRegistry.get().examples` instead.
24+
- Consider [the automated migration](https://www.npmjs.com/package/@express-zod-api/migration).
25+
26+
```diff
27+
- z.string().meta({ example: "test" });
28+
- z.string().meta({ examples: { one: { value: "test" } } });
29+
+ z.string().meta({ examples: ["test"] });
30+
+ z.string().example("test").example("another"); // plugin method
31+
```
32+
33+
```diff
34+
- getExamples(schema);
35+
+ schema.meta()?.examples || [];
36+
+ globalRegistry.get(schema)?.examples || [];
37+
```
38+
339
## Version 24
440

541
### v24.7.3

README.md

Lines changed: 16 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -58,8 +58,7 @@ Start your API server with I/O schema validation and custom middlewares in minut
5858
5. [Graceful shutdown](#graceful-shutdown)
5959
6. [Subscriptions](#subscriptions)
6060
8. [Caveats](#caveats)
61-
1. [Zod 4 schema assignment error](#zod-4-schema-assignment-error)
62-
2. [Excessive properties in endpoint output](#excessive-properties-in-endpoint-output)
61+
1. [Excessive properties in endpoint output](#excessive-properties-in-endpoint-output)
6362
9. [Your input to my output](#your-input-to-my-output)
6463

6564
See also [Changelog](CHANGELOG.md) and [automated migration](https://www.npmjs.com/package/@express-zod-api/migration).
@@ -193,8 +192,6 @@ Ensure having the following options in order to make it work as expected:
193192
}
194193
```
195194

196-
See also how `moduleResolution` may cause [Zod 4 schema assignment error](#zod-4-schema-assignment-error).
197-
198195
## Set up config
199196

200197
Create a minimal configuration. Find out all configurable options
@@ -216,7 +213,7 @@ Learn how to make factories for [custom response](#response-customization) and b
216213

217214
```typescript
218215
import { defaultEndpointsFactory } from "express-zod-api";
219-
import { z } from "zod/v4";
216+
import { z } from "zod";
220217

221218
const helloWorldEndpoint = defaultEndpointsFactory.build({
222219
// method: "get" (default) or array ["get", "post", ...]
@@ -328,7 +325,7 @@ Inputs of middlewares are also available to endpoint handlers within `input`.
328325
Here is an example of the authentication middleware, that checks a `key` from input and `token` from headers:
329326

330327
```typescript
331-
import { z } from "zod/v4";
328+
import { z } from "zod";
332329
import createHttpError from "http-errors";
333330
import { Middleware } from "express-zod-api";
334331

@@ -458,7 +455,7 @@ You can implement additional validations within schemas using refinements.
458455
Validation errors are reported in a response with a status code `400`.
459456

460457
```typescript
461-
import { z } from "zod/v4";
458+
import { z } from "zod";
462459
import { Middleware } from "express-zod-api";
463460

464461
const nicknameConstraintMiddleware = new Middleware({
@@ -499,7 +496,7 @@ Since parameters of GET requests come in the form of strings, there is often a n
499496
arrays of numbers.
500497

501498
```typescript
502-
import { z } from "zod/v4";
499+
import { z } from "zod";
503500

504501
const getUserEndpoint = endpointsFactory.build({
505502
input: z.object({
@@ -530,7 +527,7 @@ Here is a recommended solution: it is important to use shallow transformations o
530527
```ts
531528
import camelize from "camelize-ts";
532529
import snakify from "snakify-ts";
533-
import { z } from "zod/v4";
530+
import { z } from "zod";
534531

535532
const endpoint = endpointsFactory.build({
536533
input: z
@@ -583,7 +580,7 @@ provides your endpoint handler or middleware with a `Date`. It supports the foll
583580
format for the response transmission. Both schemas accept metadata as an argument. Consider the following example:
584581

585582
```typescript
586-
import { z } from "zod/v4";
583+
import { z } from "zod";
587584
import { ez, defaultEndpointsFactory } from "express-zod-api";
588585

589586
const updateUserEndpoint = defaultEndpointsFactory.build({
@@ -758,7 +755,7 @@ In a similar way you can enable request headers as the input source. This is an
758755

759756
```typescript
760757
import { createConfig, Middleware } from "express-zod-api";
761-
import { z } from "zod/v4";
758+
import { z } from "zod";
762759

763760
createConfig({
764761
inputSources: {
@@ -793,7 +790,7 @@ type DefaultResponse<OUT> =
793790
You can create your own result handler by using this example as a template:
794791

795792
```typescript
796-
import { z } from "zod/v4";
793+
import { z } from "zod";
797794
import {
798795
ResultHandler,
799796
ensureHttpError,
@@ -918,7 +915,7 @@ which is `express.urlencoded()` by default. The request content type should be `
918915

919916
```ts
920917
import { defaultEndpointsFactory, ez } from "express-zod-api";
921-
import { z } from "zod/v4";
918+
import { z } from "zod";
922919

923920
export const submitFeedbackEndpoint = defaultEndpointsFactory.build({
924921
method: "post",
@@ -958,7 +955,7 @@ const config = createConfig({
958955
Then use `ez.upload()` schema for a corresponding property. The request content type must be `multipart/form-data`:
959956

960957
```typescript
961-
import { z } from "zod/v4";
958+
import { z } from "zod";
962959
import { ez, defaultEndpointsFactory } from "express-zod-api";
963960

964961
const fileUploadEndpoint = defaultEndpointsFactory.build({
@@ -1036,7 +1033,7 @@ from outputs of previous middlewares, if the one being tested somehow depends on
10361033
either by `errorHandler` configured within given `configProps` or `defaultResultHandler`.
10371034

10381035
```typescript
1039-
import { z } from "zod/v4";
1036+
import { z } from "zod";
10401037
import { Middleware, testMiddleware } from "express-zod-api";
10411038

10421039
const middleware = new Middleware({
@@ -1179,7 +1176,7 @@ You can also deprecate all routes the `Endpoint` assigned to by setting `Endpoin
11791176

11801177
```ts
11811178
import { Routing, DependsOnMethod } from "express-zod-api";
1182-
import { z } from "zod/v4";
1179+
import { z } from "zod";
11831180

11841181
const someEndpoint = factory.build({
11851182
deprecated: true, // deprecates all routes the endpoint assigned to
@@ -1204,7 +1201,7 @@ need to reuse a handling rule for multiple brands, use the exposed types `Depict
12041201

12051202
```ts
12061203
import ts from "typescript";
1207-
import { z } from "zod/v4";
1204+
import { z } from "zod";
12081205
import {
12091206
Documentation,
12101207
Integration,
@@ -1361,7 +1358,7 @@ Client application can subscribe to the event stream using `EventSource` class i
13611358
the implementation emitting the `time` event each second.
13621359

13631360
```typescript
1364-
import { z } from "zod/v4";
1361+
import { z } from "zod";
13651362
import { EventStreamFactory } from "express-zod-api";
13661363
import { setTimeout } from "node:timers/promises";
13671364

@@ -1386,18 +1383,6 @@ framework, [Zod Sockets](https://github.com/RobinTail/zod-sockets), which has si
13861383
There are some well-known issues and limitations, or third party bugs that cannot be fixed in the usual way, but you
13871384
should be aware of them.
13881385

1389-
## Zod 4 schema assignment error
1390-
1391-
When using `zod@^4` and doing `import { z } from "zod"` you may encounter the following error:
1392-
1393-
```text
1394-
TS2739: ZodObject<...> is missing the following properties from ZodType<...>: example, deprecated.
1395-
```
1396-
1397-
In this case make sure the `moduleResolution` in your `tsconfig.json` is either `node16`, `nodenext` or `bundler`.
1398-
Consider the [recommended tsconfig base, Node 20+](https://github.com/tsconfig/bases/blob/main/bases/node20.json).
1399-
Otherwise, keep importing `from "zod/v4"` or consider upgrading the framework to v25.
1400-
14011386
## Excessive properties in endpoint output
14021387

14031388
The schema validator removes excessive properties by default. However, Typescript
@@ -1406,7 +1391,7 @@ in this case during development. You can achieve this verification by assigning
14061391
reusing it in forced type of the output:
14071392

14081393
```typescript
1409-
import { z } from "zod/v4";
1394+
import { z } from "zod";
14101395

14111396
const output = z.object({
14121397
anything: z.number(),

SECURITY.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
| Version | Code name | Release | Supported |
66
| ------: | :------------ | :------ | :----------------: |
7+
| 25.x.x | Sara | 08.2025 | :white_check_mark: |
78
| 24.x.x | Ashley | 06.2025 | :white_check_mark: |
89
| 23.x.x | Sonia | 04.2025 | :white_check_mark: |
910
| 22.x.x | Tai | 01.2025 | :white_check_mark: |

compat-test/eslint.config.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,5 @@ import migration from "@express-zod-api/migration";
33

44
export default [
55
{ languageOptions: { parser }, plugins: { migration } },
6-
{ files: ["**/*.ts"], rules: { "migration/v24": "error" } },
6+
{ files: ["**/*.ts"], rules: { "migration/v25": "error" } },
77
];

compat-test/migration.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,6 @@ import { describe, test, expect } from "vitest";
44
describe("Migration", () => {
55
test("should fix the import", async () => {
66
const fixed = await readFile("./sample.ts", "utf-8");
7-
expect(fixed).toBe('import {} from "zod/v4";\n');
7+
expect(fixed).toBe('import {} from "zod";\n');
88
});
99
});

compat-test/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"type": "module",
44
"private": true,
55
"scripts": {
6-
"pretest": "echo 'import {} from \"zod\";' > sample.ts",
6+
"pretest": "echo 'import {} from \"zod/v4\";' > sample.ts",
77
"test": "eslint --fix && vitest --run",
88
"posttest": "rm sample.ts"
99
},
@@ -15,6 +15,6 @@
1515
"http-errors": "npm:[email protected]",
1616
"typescript": "npm:[email protected]",
1717
"typescript-eslint": "npm:[email protected]",
18-
"zod": "npm:zod@3.25.35"
18+
"zod": "npm:zod@4.0.0"
1919
}
2020
}

eslint.config.js

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,6 @@ const importConcerns = [
2323
selector: "ImportDeclaration[source.value=/^zod/] > ImportDefaultSpecifier",
2424
message: "do import { z } instead",
2525
},
26-
{
27-
selector: "ImportDeclaration[source.value='zod'] > ImportSpecifier",
28-
message: "should import from zod/v4", // @todo remove when zod version changed to 4.0.0
29-
},
3026
...builtinModules.map((mod) => ({
3127
selector: `ImportDeclaration[source.value='${mod}']`,
3228
message: `use node:${mod} for the built-in module`,

example/endpoints/accept-raw.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { z } from "zod/v4";
1+
import { z } from "zod";
22
import { defaultEndpointsFactory, ez } from "express-zod-api";
33

44
export const rawAcceptingEndpoint = defaultEndpointsFactory.build({

0 commit comments

Comments
 (0)