express-zod-api 23.5.0 → 24.0.0-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,62 @@
1
1
  # Changelog
2
2
 
3
+ ## Version 24
4
+
5
+ ### v24.0.0
6
+
7
+ - Switched to Zod 4:
8
+ - Minimum supported version of `zod` is 3.25.1, BUT imports MUST be from `zod/v4`;
9
+ - Explanation of the versioning strategy: https://github.com/colinhacks/zod/issues/4371;
10
+ - Express Zod API, however, is not aiming to support both Zod 3 and Zod 4 simultaneously due to:
11
+ - incompatibility of data structures;
12
+ - operating composite schemas (need to avoid mixing schemas of different versions);
13
+ - the temporary nature of this transition;
14
+ - the advantages of Zod 4 that provide opportunities to simplifications and corrections of known issues.
15
+ - `IOSchema` type had to be simplified down to a schema resulting to an `object`, but not an `array`;
16
+ - Refer to [Migration guide on Zod 4](https://v4.zod.dev/v4/changelog) for adjusting your schemas;
17
+ - Changes to `ZodType::example()` (Zod plugin method):
18
+ - Now acts as an alias for `ZodType::meta({ examples })`;
19
+ - The argument has to be the output type of the schema (used to be the opposite):
20
+ - This change is only breaking for transforming schemas;
21
+ - In order to specify an input example for a transforming schema the `.example()` method must be called before it;
22
+ - Generating Documentation is mostly delegated to Zod 4 `z.toJSONSchema()`:
23
+ - The basic depiction of each schema is now natively performed by Zod 4;
24
+ - Express Zod API implements some overrides and improvements to fit it into OpenAPI 3.1 that extends JSON Schema;
25
+ - The `numericRange` option removed from `Documentation` class constructor argument;
26
+ - The `brandHandling` should consist of postprocessing functions altering the depiction made by Zod 4;
27
+ - The `Depicter` type signature changed;
28
+ - The `optionalPropStyle` option removed from `Integration` class constructor:
29
+ - Use `.optional()` to add question mark to the object property as well as `undefined` to its type;
30
+ - Use `.or(z.undefined())` to add `undefined` to the type of the object property;
31
+ - Reasoning: https://x.com/colinhacks/status/1919292504861491252;
32
+ - `z.any()` and `z.unknown()` are not optional, details: https://v4.zod.dev/v4/changelog#changes-zunknown-optionality.
33
+ - The `getExamples()` public helper removed — use `.meta()?.examples` instead;
34
+ - Consider the automated migration using the built-in ESLint rule.
35
+
36
+ ```js
37
+ // eslint.config.mjs — minimal ESLint 9 config to apply migrations automatically using "eslint --fix"
38
+ import parser from "@typescript-eslint/parser";
39
+ import migration from "express-zod-api/migration";
40
+
41
+ export default [
42
+ { languageOptions: { parser }, plugins: { migration } },
43
+ { files: ["**/*.ts"], rules: { "migration/v24": "error" } },
44
+ ];
45
+ ```
46
+
47
+ ```diff
48
+ - import { z } from "zod";
49
+ + import { z } from "zod/v4";
50
+ ```
51
+
52
+ ```diff
53
+ z.string()
54
+ + .example("123")
55
+ .transform(Number)
56
+ - .example("123")
57
+ + .example(123)
58
+ ```
59
+
3
60
  ## Version 23
4
61
 
5
62
  ### v23.5.0
package/README.md CHANGED
@@ -58,8 +58,7 @@ Start your API server with I/O schema validation and custom middlewares in minut
58
58
  5. [Deprecated schemas and routes](#deprecated-schemas-and-routes)
59
59
  6. [Customizable brands handling](#customizable-brands-handling)
60
60
  8. [Caveats](#caveats)
61
- 1. [Coercive schema of Zod](#coercive-schema-of-zod)
62
- 2. [Excessive properties in endpoint output](#excessive-properties-in-endpoint-output)
61
+ 1. [Excessive properties in endpoint output](#excessive-properties-in-endpoint-output)
63
62
  9. [Your input to my output](#your-input-to-my-output)
64
63
 
65
64
  You can find the release notes and migration guides in [Changelog](CHANGELOG.md).
@@ -150,7 +149,8 @@ Much can be customized to fit your needs.
150
149
 
151
150
  - [Typescript](https://www.typescriptlang.org/) first.
152
151
  - Web server — [Express.js](https://expressjs.com/) v5.
153
- - Schema validation — [Zod 3.x](https://github.com/colinhacks/zod) including [Zod Plugin](#zod-plugin).
152
+ - Schema validation — [Zod 4.x](https://github.com/colinhacks/zod) including [Zod Plugin](#zod-plugin):
153
+ - For using with Zod 3.x install the framework versions below 24.0.0.
154
154
  - Supports any logger having `info()`, `debug()`, `error()` and `warn()` methods;
155
155
  - Built-in console logger with colorful and pretty inspections by default.
156
156
  - Generators:
@@ -168,7 +168,7 @@ Install the framework, its peer dependencies and type assistance packages using
168
168
 
169
169
  ```shell
170
170
  # example for yarn:
171
- yarn add express-zod-api express zod@3 typescript http-errors
171
+ yarn add express-zod-api express zod typescript http-errors
172
172
  yarn add -D @types/express @types/node @types/http-errors
173
173
  ```
174
174
 
@@ -213,7 +213,7 @@ import { defaultEndpointsFactory } from "express-zod-api";
213
213
  The endpoint responds with "Hello, World" or "Hello, {name}" if the name is supplied within `GET` request payload.
214
214
 
215
215
  ```typescript
216
- import { z } from "zod";
216
+ import { z } from "zod/v4";
217
217
 
218
218
  const helloWorldEndpoint = defaultEndpointsFactory.build({
219
219
  // method: "get" (default) or array ["get", "post", ...]
@@ -325,7 +325,7 @@ Inputs of middlewares are also available to endpoint handlers within `input`.
325
325
  Here is an example of the authentication middleware, that checks a `key` from input and `token` from headers:
326
326
 
327
327
  ```typescript
328
- import { z } from "zod";
328
+ import { z } from "zod/v4";
329
329
  import createHttpError from "http-errors";
330
330
  import { Middleware } from "express-zod-api";
331
331
 
@@ -455,7 +455,7 @@ You can implement additional validations within schemas using refinements.
455
455
  Validation errors are reported in a response with a status code `400`.
456
456
 
457
457
  ```typescript
458
- import { z } from "zod";
458
+ import { z } from "zod/v4";
459
459
  import { Middleware } from "express-zod-api";
460
460
 
461
461
  const nicknameConstraintMiddleware = new Middleware({
@@ -496,7 +496,7 @@ Since parameters of GET requests come in the form of strings, there is often a n
496
496
  arrays of numbers.
497
497
 
498
498
  ```typescript
499
- import { z } from "zod";
499
+ import { z } from "zod/v4";
500
500
 
501
501
  const getUserEndpoint = endpointsFactory.build({
502
502
  input: z.object({
@@ -527,7 +527,7 @@ Here is a recommended solution: it is important to use shallow transformations o
527
527
  ```ts
528
528
  import camelize from "camelize-ts";
529
529
  import snakify from "snakify-ts";
530
- import { z } from "zod";
530
+ import { z } from "zod/v4";
531
531
 
532
532
  const endpoint = endpointsFactory.build({
533
533
  input: z
@@ -580,7 +580,7 @@ provides your endpoint handler or middleware with a `Date`. It supports the foll
580
580
  format for the response transmission. Consider the following simplified example for better understanding:
581
581
 
582
582
  ```typescript
583
- import { z } from "zod";
583
+ import { z } from "zod/v4";
584
584
  import { ez, defaultEndpointsFactory } from "express-zod-api";
585
585
 
586
586
  const updateUserEndpoint = defaultEndpointsFactory.build({
@@ -790,7 +790,7 @@ In a similar way you can enable request headers as the input source. This is an
790
790
 
791
791
  ```typescript
792
792
  import { createConfig, Middleware } from "express-zod-api";
793
- import { z } from "zod";
793
+ import { z } from "zod/v4";
794
794
 
795
795
  createConfig({
796
796
  inputSources: {
@@ -825,7 +825,7 @@ type DefaultResponse<OUT> =
825
825
  You can create your own result handler by using this example as a template:
826
826
 
827
827
  ```typescript
828
- import { z } from "zod";
828
+ import { z } from "zod/v4";
829
829
  import {
830
830
  ResultHandler,
831
831
  ensureHttpError,
@@ -951,7 +951,7 @@ which is `express.urlencoded()` by default. The request content type should be `
951
951
 
952
952
  ```ts
953
953
  import { defaultEndpointsFactory, ez } from "express-zod-api";
954
- import { z } from "zod";
954
+ import { z } from "zod/v4";
955
955
 
956
956
  export const submitFeedbackEndpoint = defaultEndpointsFactory.build({
957
957
  method: "post",
@@ -991,7 +991,7 @@ const config = createConfig({
991
991
  Then use `ez.upload()` schema for a corresponding property. The request content type must be `multipart/form-data`:
992
992
 
993
993
  ```typescript
994
- import { z } from "zod";
994
+ import { z } from "zod/v4";
995
995
  import { ez, defaultEndpointsFactory } from "express-zod-api";
996
996
 
997
997
  const fileUploadEndpoint = defaultEndpointsFactory.build({
@@ -1069,7 +1069,7 @@ from outputs of previous middlewares, if the one being tested somehow depends on
1069
1069
  either by `errorHandler` configured within given `configProps` or `defaultResultHandler`.
1070
1070
 
1071
1071
  ```typescript
1072
- import { z } from "zod";
1072
+ import { z } from "zod/v4";
1073
1073
  import { Middleware, testMiddleware } from "express-zod-api";
1074
1074
 
1075
1075
  const middleware = new Middleware({
@@ -1180,7 +1180,7 @@ Client application can subscribe to the event stream using `EventSource` class i
1180
1180
  the implementation emitting the `time` event each second.
1181
1181
 
1182
1182
  ```typescript
1183
- import { z } from "zod";
1183
+ import { z } from "zod/v4";
1184
1184
  import { EventStreamFactory } from "express-zod-api";
1185
1185
  import { setTimeout } from "node:timers/promises";
1186
1186
 
@@ -1223,7 +1223,6 @@ import { Integration } from "express-zod-api";
1223
1223
  const client = new Integration({
1224
1224
  routing,
1225
1225
  variant: "client", // <— optional, see also "types" for a DIY solution
1226
- optionalPropStyle: { withQuestionMark: true, withUndefined: true }, // optional
1227
1226
  });
1228
1227
 
1229
1228
  const prettierFormattedTypescriptCode = await client.printFormatted(); // or just .print() for unformatted
@@ -1272,7 +1271,11 @@ const exampleEndpoint = defaultEndpointsFactory.build({
1272
1271
  shortDescription: "Retrieves the user.", // <—— this becomes the summary line
1273
1272
  description: "The detailed explanaition on what this endpoint does.",
1274
1273
  input: z.object({
1275
- id: z.number().describe("the ID of the user").example(123),
1274
+ id: z
1275
+ .string()
1276
+ .example("123") // input examples should be set before transformations
1277
+ .transform(Number)
1278
+ .describe("the ID of the user"),
1276
1279
  }),
1277
1280
  // ..., similarly for output and middlewares
1278
1281
  });
@@ -1321,7 +1324,7 @@ You can also deprecate all routes the `Endpoint` assigned to by setting `Endpoin
1321
1324
 
1322
1325
  ```ts
1323
1326
  import { Routing, DependsOnMethod } from "express-zod-api";
1324
- import { z } from "zod";
1327
+ import { z } from "zod/v4";
1325
1328
 
1326
1329
  const someEndpoint = factory.build({
1327
1330
  deprecated: true, // deprecates all routes the endpoint assigned to
@@ -1346,7 +1349,7 @@ need to reuse a handling rule for multiple brands, use the exposed types `Depict
1346
1349
 
1347
1350
  ```ts
1348
1351
  import ts from "typescript";
1349
- import { z } from "zod";
1352
+ import { z } from "zod/v4";
1350
1353
  import {
1351
1354
  Documentation,
1352
1355
  Integration,
@@ -1358,12 +1361,12 @@ const myBrand = Symbol("MamaToldMeImSpecial"); // I recommend to use symbols for
1358
1361
  const myBrandedSchema = z.string().brand(myBrand);
1359
1362
 
1360
1363
  const ruleForDocs: Depicter = (
1361
- schema: typeof myBrandedSchema, // you should assign type yourself
1362
- { next, path, method, isResponse }, // handle a nested schema using next()
1363
- ) => {
1364
- const defaultDepiction = next(schema.unwrap()); // { type: string }
1365
- return { summary: "Special type of data" };
1366
- };
1364
+ { zodSchema, jsonSchema }, // jsonSchema is the default depiction
1365
+ { path, method, isResponse },
1366
+ ) => ({
1367
+ ...jsonSchema,
1368
+ summary: "Special type of data",
1369
+ });
1367
1370
 
1368
1371
  const ruleForClient: Producer = (
1369
1372
  schema: typeof myBrandedSchema, // you should assign type yourself
@@ -1384,16 +1387,6 @@ new Integration({
1384
1387
  There are some well-known issues and limitations, or third party bugs that cannot be fixed in the usual way, but you
1385
1388
  should be aware of them.
1386
1389
 
1387
- ## Coercive schema of Zod
1388
-
1389
- Despite being supported by the framework, `z.coerce.*` schema
1390
- [does not work intuitively](https://github.com/RobinTail/express-zod-api/issues/759).
1391
- Please be aware that `z.coerce.number()` and `z.number({ coerce: true })` (being typed not well) still will NOT allow
1392
- you to assign anything but number. Moreover, coercive schemas are not fail-safe and their methods `.isOptional()` and
1393
- `.isNullable()` [are buggy](https://github.com/colinhacks/zod/issues/1911). If possible, try to avoid using this type
1394
- of schema. This issue [will NOT be fixed](https://github.com/colinhacks/zod/issues/1760#issuecomment-1407816838) in
1395
- Zod version 3.x.
1396
-
1397
1390
  ## Excessive properties in endpoint output
1398
1391
 
1399
1392
  The schema validator removes excessive properties by default. However, Typescript
@@ -1402,7 +1395,7 @@ in this case during development. You can achieve this verification by assigning
1402
1395
  reusing it in forced type of the output:
1403
1396
 
1404
1397
  ```typescript
1405
- import { z } from "zod";
1398
+ import { z } from "zod/v4";
1406
1399
 
1407
1400
  const output = z.object({
1408
1401
  anything: z.number(),
package/SECURITY.md CHANGED
@@ -4,6 +4,7 @@
4
4
 
5
5
  | Version | Code name | Release | Supported |
6
6
  | ------: | :------------ | :------ | :----------------: |
7
+ | 24.x.x | Ashley | 06.2025 | :white_check_mark: |
7
8
  | 23.x.x | Sonia | 04.2025 | :white_check_mark: |
8
9
  | 22.x.x | Tai | 01.2025 | :white_check_mark: |
9
10
  | 21.x.x | Kesaria | 11.2024 | :white_check_mark: |