Skip to content
Merged
Show file tree
Hide file tree
Changes from 55 commits
Commits
Show all changes
65 commits
Select commit Hold shift + click to select a range
f89e8a7
Updating trigger branches.
RobinTail Jun 13, 2025
dc7bd91
Reusing `z.object({})` for describing `EmptySchema` and `EmptyObject`…
RobinTail Jun 13, 2025
e6ce763
Removing depictEnum() and depictLiteral(). (#2742)
RobinTail Jun 13, 2025
a9ee960
Using `undici` 7 (#2622)
RobinTail Jun 13, 2025
899a56c
Merge branch 'master' into make-v25
RobinTail Jun 17, 2025
d426270
also rm unstubAllGlobals().
RobinTail Jun 17, 2025
83b2afc
Merge branch 'master' into make-v25
RobinTail Jun 18, 2025
ea40d93
Merge branch 'master' into make-v25
RobinTail Jun 19, 2025
8de8423
Merge branch 'master' into make-v25
RobinTail Jun 24, 2025
35394c2
Using Vite 7 (#2711)
RobinTail Jun 24, 2025
caae076
Merge branch 'master' into make-v25
RobinTail Jun 26, 2025
726b55f
Merge branch 'master' into make-v25
RobinTail Jun 28, 2025
25667c2
Merge branch 'master' into make-v25
RobinTail Jun 29, 2025
983260a
Merge branch 'master' into make-v25
RobinTail Jun 29, 2025
fb62f90
Merge branch 'master' into make-v25
RobinTail Jun 30, 2025
8ccf714
feat(v25): `undefined` input schema for factories and middlewares by …
RobinTail Jun 30, 2025
a6593fa
Merge branch 'master' into make-v25
RobinTail Jul 1, 2025
32e93a8
Changelog: 25.0.0.
RobinTail Jul 1, 2025
3204822
v25 is dedicated to Sara Millerey González.
RobinTail Jul 1, 2025
f4fe262
express-zod-api version 25.0.0-beta.1
github-actions[bot] Jul 1, 2025
aad8983
Empty migration base for v25.
RobinTail Jul 1, 2025
87285be
Restoring zod import migration for v25.
RobinTail Jul 1, 2025
d023fec
Merge branch 'master' into make-v25
RobinTail Jul 1, 2025
4b50df3
Merge branch 'master' into make-v25
RobinTail Jul 2, 2025
21b6c2f
Merge branch 'master' into make-v25
RobinTail Jul 5, 2025
b981cbd
Upgrading vite to 7.0.2.
RobinTail Jul 5, 2025
78686d6
Merge branch 'master' into make-v25
RobinTail Jul 6, 2025
4e35eb6
migration version 25.0.0-beta.1
github-actions[bot] Jul 6, 2025
5065c83
Merge branch 'master' into make-v25
RobinTail Jul 7, 2025
f224827
Merge branch 'master' into make-v25
RobinTail Jul 11, 2025
998dae0
Adjusting compat test.
RobinTail Jul 11, 2025
aada6b8
Fix migration test.
RobinTail Jul 11, 2025
955a384
Restoring `import from "zod"` (#2809)
RobinTail Jul 11, 2025
3417d9d
Merge branch 'master' into make-v25
RobinTail Jul 12, 2025
089977f
Merge branch 'master' into make-v25
RobinTail Jul 12, 2025
7c284a3
Pure ESM distribution (#2814)
RobinTail Jul 12, 2025
eeb97b1
Merge branch 'master' into make-v25
RobinTail Jul 17, 2025
4b3e60b
Upgrading vite to 7.0.5.
RobinTail Jul 17, 2025
8d55489
express-zod-api version 25.0.0-beta.2
github-actions[bot] Jul 17, 2025
9cfce7b
Merge branch 'master' into make-v25
RobinTail Jul 18, 2025
fd3e73c
express-zod-api version 25.0.0-beta.3
github-actions[bot] Jul 18, 2025
2405454
Merge branch 'master' into make-v25
RobinTail Jul 19, 2025
fbd8cb0
Merge branch 'master' into make-v25
RobinTail Jul 20, 2025
b38a63e
Merge branch 'master' into make-v25
RobinTail Jul 20, 2025
52c869d
express-zod-api version 25.0.0-beta.4
github-actions[bot] Jul 20, 2025
9bd32f5
Merge branch 'master' into make-v25
RobinTail Jul 23, 2025
8d4e6a4
Merge branch 'master' into make-v25
RobinTail Jul 25, 2025
de0438f
Merge branch 'master' into make-v25
RobinTail Jul 25, 2025
c6cd683
Merge branch 'master' into make-v25
RobinTail Jul 25, 2025
18024a9
Drop `example`, object-based `examples` and `getExamples()` (#2842)
RobinTail Jul 25, 2025
1e1741e
express-zod-api version 25.0.0-beta.5
github-actions[bot] Jul 26, 2025
89927ce
Fix migration rule name in test.
RobinTail Jul 26, 2025
d7413a9
Improving v25 migration (#2843)
RobinTail Jul 26, 2025
07213d5
migration version 25.0.0-beta.2
github-actions[bot] Jul 26, 2025
063a4ac
Accessing core types via `z.core` (#2844)
RobinTail Jul 26, 2025
5c32b6f
express-zod-api version 25.0.0-beta.6
github-actions[bot] Jul 26, 2025
69147e2
Changelog: note on TS1479 and pure ESM.
RobinTail Jul 26, 2025
5caea74
rm unused code from migration.
RobinTail Jul 26, 2025
9ebc5c3
Changelog: change order of suggested alernatives
RobinTail Jul 27, 2025
74724a4
Revert "Changelog: change order of suggested alernatives"
RobinTail Jul 27, 2025
ab46eaa
Merge branch 'master' into make-v25
RobinTail Jul 27, 2025
a9a5824
up vite to 7.0.6.
RobinTail Jul 28, 2025
dc08c4c
Merge branch 'master' into make-v25
RobinTail Aug 1, 2025
d6d8953
express-zod-api version 25.0.0-beta.7
github-actions[bot] Aug 1, 2025
7bead0a
adjusting trigger branches.
RobinTail Aug 1, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/codeql-analysis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@ name: "CodeQL"

on:
push:
branches: [ master, v20, v21, v22, v23 ]
branches: [ master, v21, v22, v23, make-v25 ]
pull_request:
# The branches below must be a subset of the branches above
branches: [ master, v20, v21, v22, v23 ]
branches: [ master, v21, v22, v23, make-v25 ]
schedule:
- cron: '26 8 * * 1'

Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/node.js.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,17 @@ name: Node.js CI

on:
push:
branches: [ master, v20, v21, v22, v23 ]
branches: [ master, v21, v22, v23, make-v25 ]
pull_request:
branches: [ master, v20, v21, v22, v23 ]
branches: [ master, v21, v22, v23, make-v25 ]

jobs:
build:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
node-version: [20.9.0, 20.x, 22.0.0, 22.x, 24.0.0, 24.x]
node-version: [20.19.0, 20.x, 22.12.0, 22.x, 24.0.0, 24.x]
# See supported Node.js release schedule at https://nodejs.org/en/about/releases/
steps:
- name: Checkout
Expand Down
32 changes: 32 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,37 @@
# Changelog

## Version 25

### v25.0.0

- Supported Node.js versions: `^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;
- Supported `zod` version: `^4.0.0`;
- Compatibility with `zod@^3` is dropped;
- You SHOULD now `import { z } from "zod"` without the `/v4` suffix;
- Changes to the Zod plugin and metadata processing:
- Dropped support of examples that are given as `example` property of `.meta()` argument;
- Dropped support of examples given within an object-based value of `examples` property of `.meta()` argument;
- Use either `.example()` method or `.meta()` method with `examples` property being an array;
- Changes to the `Middleware` class:
- When the `input` schema is not defined, the `input` argument of the `handler` method is now `unknown`;
- Changes to publicly exposed method:
- The `getExamples()` helper is removed, use `.meta().examples` or `globalRegistry.get().examples` instead.

```diff
- z.string().meta({ example: "test" });
- z.string().meta({ examples: { one: { value: "test" } } });
+ z.string().meta({ examples: ["test"] });
+ z.string().example("test").example("another"); // plugin method
```

```diff
- getExamples(schema);
+ schema.meta()?.examples || [];
+ globalRegistry.get(schema)?.examples || [];
```

## Version 24

### v24.7.1
Expand Down
47 changes: 16 additions & 31 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,7 @@ Start your API server with I/O schema validation and custom middlewares in minut
5. [Graceful shutdown](#graceful-shutdown)
6. [Subscriptions](#subscriptions)
8. [Caveats](#caveats)
1. [Zod 4 schema assignment error](#zod-4-schema-assignment-error)
2. [Excessive properties in endpoint output](#excessive-properties-in-endpoint-output)
1. [Excessive properties in endpoint output](#excessive-properties-in-endpoint-output)
9. [Your input to my output](#your-input-to-my-output)

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

See also how `moduleResolution` may cause [Zod 4 schema assignment error](#zod-4-schema-assignment-error).

## Set up config

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

```typescript
import { defaultEndpointsFactory } from "express-zod-api";
import { z } from "zod/v4";
import { z } from "zod";

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

```typescript
import { z } from "zod/v4";
import { z } from "zod";
import createHttpError from "http-errors";
import { Middleware } from "express-zod-api";

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

```typescript
import { z } from "zod/v4";
import { z } from "zod";
import { Middleware } from "express-zod-api";

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

```typescript
import { z } from "zod/v4";
import { z } from "zod";

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

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

```typescript
import { z } from "zod/v4";
import { z } from "zod";
import { ez, defaultEndpointsFactory } from "express-zod-api";

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

```typescript
import { createConfig, Middleware } from "express-zod-api";
import { z } from "zod/v4";
import { z } from "zod";

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

```typescript
import { z } from "zod/v4";
import { z } from "zod";
import {
ResultHandler,
ensureHttpError,
Expand Down Expand Up @@ -918,7 +915,7 @@ which is `express.urlencoded()` by default. The request content type should be `

```ts
import { defaultEndpointsFactory, ez } from "express-zod-api";
import { z } from "zod/v4";
import { z } from "zod";

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

```typescript
import { z } from "zod/v4";
import { z } from "zod";
import { ez, defaultEndpointsFactory } from "express-zod-api";

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

```typescript
import { z } from "zod/v4";
import { z } from "zod";
import { Middleware, testMiddleware } from "express-zod-api";

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

```ts
import { Routing, DependsOnMethod } from "express-zod-api";
import { z } from "zod/v4";
import { z } from "zod";

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

```ts
import ts from "typescript";
import { z } from "zod/v4";
import { z } from "zod";
import {
Documentation,
Integration,
Expand Down Expand Up @@ -1361,7 +1358,7 @@ Client application can subscribe to the event stream using `EventSource` class i
the implementation emitting the `time` event each second.

```typescript
import { z } from "zod/v4";
import { z } from "zod";
import { EventStreamFactory } from "express-zod-api";
import { setTimeout } from "node:timers/promises";

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

## Zod 4 schema assignment error

When using `zod@^4` and doing `import { z } from "zod"` you may encounter the following error:

```text
TS2739: ZodObject<...> is missing the following properties from ZodType<...>: example, deprecated.
```

In this case make sure the `moduleResolution` in your `tsconfig.json` is either `node16`, `nodenext` or `bundler`.
Consider the [recommended tsconfig base, Node 20+](https://github.com/tsconfig/bases/blob/main/bases/node20.json).
Otherwise, keep importing `from "zod/v4"` or consider upgrading the framework to v25.

## Excessive properties in endpoint output

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

```typescript
import { z } from "zod/v4";
import { z } from "zod";

const output = z.object({
anything: z.number(),
Expand Down
1 change: 1 addition & 0 deletions SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

| Version | Code name | Release | Supported |
| ------: | :------------ | :------ | :----------------: |
| 25.x.x | Sara | 08.2025 | :white_check_mark: |
| 24.x.x | Ashley | 06.2025 | :white_check_mark: |
| 23.x.x | Sonia | 04.2025 | :white_check_mark: |
| 22.x.x | Tai | 01.2025 | :white_check_mark: |
Expand Down
2 changes: 1 addition & 1 deletion compat-test/eslint.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,5 @@ import migration from "@express-zod-api/migration";

export default [
{ languageOptions: { parser }, plugins: { migration } },
{ files: ["**/*.ts"], rules: { "migration/v24": "error" } },
{ files: ["**/*.ts"], rules: { "migration/v25": "error" } },
];
2 changes: 1 addition & 1 deletion compat-test/migration.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@ import { describe, test, expect } from "vitest";
describe("Migration", () => {
test("should fix the import", async () => {
const fixed = await readFile("./sample.ts", "utf-8");
expect(fixed).toBe('import {} from "zod/v4";\n');
expect(fixed).toBe('import {} from "zod";\n');
});
});
4 changes: 2 additions & 2 deletions compat-test/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"type": "module",
"private": true,
"scripts": {
"pretest": "echo 'import {} from \"zod\";' > sample.ts",
"pretest": "echo 'import {} from \"zod/v4\";' > sample.ts",
"test": "eslint --fix && vitest --run",
"posttest": "rm sample.ts"
},
Expand All @@ -15,6 +15,6 @@
"http-errors": "npm:[email protected]",
"typescript": "npm:[email protected]",
"typescript-eslint": "npm:[email protected]",
"zod": "npm:zod@3.25.35"
"zod": "npm:zod@4.0.0"
}
}
4 changes: 0 additions & 4 deletions eslint.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,6 @@ const importConcerns = [
selector: "ImportDeclaration[source.value=/^zod/] > ImportDefaultSpecifier",
message: "do import { z } instead",
},
{
selector: "ImportDeclaration[source.value='zod'] > ImportSpecifier",
message: "should import from zod/v4", // @todo remove when zod version changed to 4.0.0
},
...builtinModules.map((mod) => ({
selector: `ImportDeclaration[source.value='${mod}']`,
message: `use node:${mod} for the built-in module`,
Expand Down
2 changes: 1 addition & 1 deletion example/endpoints/accept-raw.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { z } from "zod/v4";
import { z } from "zod";
import { defaultEndpointsFactory, ez } from "express-zod-api";

export const rawAcceptingEndpoint = defaultEndpointsFactory.build({
Expand Down
2 changes: 1 addition & 1 deletion example/endpoints/create-user.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import createHttpError from "http-errors";
import assert from "node:assert/strict";
import { z } from "zod/v4";
import { z } from "zod";
import { statusDependingFactory } from "../factories";

const namePart = z.string().regex(/^\w+$/);
Expand Down
2 changes: 1 addition & 1 deletion example/endpoints/delete-user.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import createHttpError from "http-errors";
import assert from "node:assert/strict";
import { z } from "zod/v4";
import { z } from "zod";
import { noContentFactory } from "../factories";

/** @desc The endpoint demonstrates no content response established by its factory */
Expand Down
2 changes: 1 addition & 1 deletion example/endpoints/list-users.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { z } from "zod/v4";
import { z } from "zod";
import { arrayRespondingFactory } from "../factories";

/**
Expand Down
2 changes: 1 addition & 1 deletion example/endpoints/retrieve-user.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import createHttpError from "http-errors";
import assert from "node:assert/strict";
import { z } from "zod/v4";
import { z } from "zod";
import { defaultEndpointsFactory } from "express-zod-api";
import { methodProviderMiddleware } from "../middlewares";

Expand Down
2 changes: 1 addition & 1 deletion example/endpoints/send-avatar.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { z } from "zod/v4";
import { z } from "zod";
import { fileSendingEndpointsFactory } from "../factories";
import { readFile } from "node:fs/promises";

Expand Down
2 changes: 1 addition & 1 deletion example/endpoints/stream-avatar.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { z } from "zod/v4";
import { z } from "zod";
import { fileStreamingEndpointsFactory } from "../factories";

export const streamAvatarEndpoint = fileStreamingEndpointsFactory.build({
Expand Down
2 changes: 1 addition & 1 deletion example/endpoints/submit-feedback.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { defaultEndpointsFactory, ez } from "express-zod-api";
import { z } from "zod/v4";
import { z } from "zod";

export const submitFeedbackEndpoint = defaultEndpointsFactory.build({
method: "post",
Expand Down
2 changes: 1 addition & 1 deletion example/endpoints/time-subscription.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { z } from "zod/v4";
import { z } from "zod";
import { setTimeout } from "node:timers/promises";
import { eventsFactory } from "../factories";

Expand Down
2 changes: 1 addition & 1 deletion example/endpoints/update-user.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import createHttpError from "http-errors";
import assert from "node:assert/strict";
import { z } from "zod/v4";
import { z } from "zod";
import { ez } from "express-zod-api";
import { keyAndTokenAuthenticatedEndpointsFactory } from "../factories";

Expand Down
2 changes: 1 addition & 1 deletion example/endpoints/upload-avatar.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { z } from "zod/v4";
import { z } from "zod";
import { defaultEndpointsFactory, ez } from "express-zod-api";
import { createHash } from "node:crypto";

Expand Down
2 changes: 1 addition & 1 deletion example/factories.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ import {
} from "express-zod-api";
import { authMiddleware } from "./middlewares";
import { createReadStream } from "node:fs";
import { z } from "zod";
import { stat } from "node:fs/promises";
import { z } from "zod/v4";

/** @desc This factory extends the default one by enforcing the authentication using the specified middleware */
export const keyAndTokenAuthenticatedEndpointsFactory =
Expand Down
Loading