express-zod-api 28.7.4 → 29.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 +63 -39
- package/README.md +27 -20
- package/SECURITY.md +2 -1
- package/dist/index.d.ts +300 -286
- package/dist/index.js +7 -18
- package/package.json +6 -6
package/CHANGELOG.md
CHANGED
|
@@ -1,7 +1,58 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## Version 29
|
|
4
|
+
|
|
5
|
+
### v29.0.0
|
|
6
|
+
|
|
7
|
+
- Supported Node.js versions: `^22.19.0 || ^24.11.0 || ^26.0.0`;
|
|
8
|
+
- Breaking change to the `cors` config option:
|
|
9
|
+
- Changed from `boolean | HeadersProvider` to `boolean | RequestHandler`;
|
|
10
|
+
- You can now use the well-known `cors` package or pass a conventional `RequestHandler` directly.
|
|
11
|
+
- Body parsers are now applied globally rather than per-endpoint (`createServer` experience only):
|
|
12
|
+
- Endpoints can accept requests in different content types (e.g., either JSON or URL-encoded body);
|
|
13
|
+
- The `Documentation` generator continues to guess the desired request type from the input schema;
|
|
14
|
+
- Parsers retain the ability for configuration via `jsonParser`, `formParser`, and `rawParser` config options;
|
|
15
|
+
- If using `attachRouting()` in a DIY server, parsers have to be installed manually.
|
|
16
|
+
- Potentially breaking changes to the server lifecycle hooks:
|
|
17
|
+
- The hooks (`beforeRouting` and `afterRouting` config options) are no longer async;
|
|
18
|
+
- `beforeRouting` now runs after the installation of globally enabled parsers;
|
|
19
|
+
- Added new `beforeParsers` hook that runs before all parsers are installed.
|
|
20
|
+
- The static async method `Integration::create()` removed — use `new Integration()` instead;
|
|
21
|
+
- The `createServer()` function is now synchronous — that should simplify the daily routines for beginners;
|
|
22
|
+
- Added HTTP QUERY method support (RFC 10008):
|
|
23
|
+
- The QUERY method is like GET but with a body — safe, idempotent, and cacheable;
|
|
24
|
+
- Default input sources for QUERY: `["query", "body", "params"]` (from the lowest priority to highest);
|
|
25
|
+
- Supported by `Integration` and `Documentation` generators.
|
|
26
|
+
- Changes to `Documentation` constructor:
|
|
27
|
+
- `serverUrl` renamed to `server` and now also accepts OpenAPI's ServerObject;
|
|
28
|
+
- `title` and `version` must be wrapped into `info`, assignable with OpenAPI's InfoObject;
|
|
29
|
+
- The Documentation generator is featuring the OpenAPI 3.2.0 with better SSE support and other features;
|
|
30
|
+
|
|
31
|
+
```diff
|
|
32
|
+
const config = createConfig({
|
|
33
|
+
- cors: () => ({ origin: "https://example.com" }),
|
|
34
|
+
+ cors: cors({ origin: "https://example.com" }), // import cors from "cors"
|
|
35
|
+
});
|
|
36
|
+
- await Integration.create({});
|
|
37
|
+
+ new Integration({});
|
|
38
|
+
- const {} = await createServer({});
|
|
39
|
+
+ const {} = createServer({});
|
|
40
|
+
new Documentation({
|
|
41
|
+
+ info: {
|
|
42
|
+
title: "Sample API",
|
|
43
|
+
version: "1.2.3",
|
|
44
|
+
+ },
|
|
45
|
+
- serverUrl: "https://example.com",
|
|
46
|
+
+ server: "https://example.com",
|
|
47
|
+
});
|
|
48
|
+
```
|
|
49
|
+
|
|
3
50
|
## Version 28
|
|
4
51
|
|
|
52
|
+
### v28.7.5
|
|
53
|
+
|
|
54
|
+
- Small performance improvement for startup and Documentation generator (cache for request type resolution).
|
|
55
|
+
|
|
5
56
|
### v28.7.4
|
|
6
57
|
|
|
7
58
|
- Centralized the `ResultHandler` error handling by `AbstractResultHandler::execute()`:
|
|
@@ -4238,9 +4289,7 @@ import { z } from "express-zod-api";
|
|
|
4238
4289
|
|
|
4239
4290
|
const endpoint = endpointsFactory.build({
|
|
4240
4291
|
input: z
|
|
4241
|
-
.object({
|
|
4242
|
-
/* ... */
|
|
4243
|
-
})
|
|
4292
|
+
.object({/* ... */})
|
|
4244
4293
|
.refine(() => true)
|
|
4245
4294
|
.refine(() => true)
|
|
4246
4295
|
.refine(() => true),
|
|
@@ -4499,9 +4548,7 @@ const myMiddleware = createMiddleware({
|
|
|
4499
4548
|
},
|
|
4500
4549
|
},
|
|
4501
4550
|
input: z.object({}),
|
|
4502
|
-
middleware: async () => ({
|
|
4503
|
-
/* ... */
|
|
4504
|
-
}),
|
|
4551
|
+
middleware: async () => ({/* ... */}),
|
|
4505
4552
|
});
|
|
4506
4553
|
|
|
4507
4554
|
// example endpoint
|
|
@@ -4510,9 +4557,7 @@ const myEndpoint = defaultEndpointsFactory.addMiddleware(myMiddleware).build({
|
|
|
4510
4557
|
method: "post",
|
|
4511
4558
|
input: z.object({}),
|
|
4512
4559
|
output: z.object({}),
|
|
4513
|
-
handler: async () => ({
|
|
4514
|
-
/* ... */
|
|
4515
|
-
}),
|
|
4560
|
+
handler: async () => ({/* ... */}),
|
|
4516
4561
|
});
|
|
4517
4562
|
```
|
|
4518
4563
|
|
|
@@ -5444,15 +5489,11 @@ const fileUploadEndpoint = defaultEndpointsFactory.build({
|
|
|
5444
5489
|
input: z.object({
|
|
5445
5490
|
avatar: z.upload(),
|
|
5446
5491
|
}),
|
|
5447
|
-
output: z.object({
|
|
5448
|
-
/* ... */
|
|
5449
|
-
}),
|
|
5492
|
+
output: z.object({/* ... */}),
|
|
5450
5493
|
handler: async ({ input: { avatar } }) => {
|
|
5451
5494
|
// avatar: {name, mv(), mimetype, encoding, data, truncated, size, etc}
|
|
5452
5495
|
// avatar.truncated is true on failure
|
|
5453
|
-
return {
|
|
5454
|
-
/* ... */
|
|
5455
|
-
};
|
|
5496
|
+
return {/* ... */};
|
|
5456
5497
|
},
|
|
5457
5498
|
});
|
|
5458
5499
|
```
|
|
@@ -5686,15 +5727,11 @@ import { EndpointOutput } from "express-zod-api";
|
|
|
5686
5727
|
|
|
5687
5728
|
const myEndpointV1 = endpointsFactory.build({
|
|
5688
5729
|
method: "get",
|
|
5689
|
-
input: z.object({
|
|
5690
|
-
/* ... */
|
|
5691
|
-
}),
|
|
5730
|
+
input: z.object({/* ... */}),
|
|
5692
5731
|
output: z.object({
|
|
5693
5732
|
name: z.string(),
|
|
5694
5733
|
}),
|
|
5695
|
-
handler: async () => ({
|
|
5696
|
-
/* ... */
|
|
5697
|
-
}),
|
|
5734
|
+
handler: async () => ({/* ... */}),
|
|
5698
5735
|
});
|
|
5699
5736
|
type MyEndpointOutput = EndpointOutput<typeof myEndpointV1>; // => { name: string }
|
|
5700
5737
|
|
|
@@ -5703,15 +5740,11 @@ import { defaultEndpointsFactory, EndpointResponse } from "express-zod-api";
|
|
|
5703
5740
|
|
|
5704
5741
|
const myEndpointV2 = defaultEndpointsFactory.build({
|
|
5705
5742
|
method: "get",
|
|
5706
|
-
input: z.object({
|
|
5707
|
-
/* ... */
|
|
5708
|
-
}),
|
|
5743
|
+
input: z.object({/* ... */}),
|
|
5709
5744
|
output: z.object({
|
|
5710
5745
|
name: z.string(),
|
|
5711
5746
|
}),
|
|
5712
|
-
handler: async () => ({
|
|
5713
|
-
/* ... */
|
|
5714
|
-
}),
|
|
5747
|
+
handler: async () => ({/* ... */}),
|
|
5715
5748
|
});
|
|
5716
5749
|
type MyEndpointResponse = EndpointResponse<typeof myEndpointV2>; // => the following type:
|
|
5717
5750
|
// {
|
|
@@ -5725,13 +5758,9 @@ type MyEndpointResponse = EndpointResponse<typeof myEndpointV2>; // => the follo
|
|
|
5725
5758
|
|
|
5726
5759
|
```ts
|
|
5727
5760
|
// before
|
|
5728
|
-
new OpenAPI({
|
|
5729
|
-
/* ... */
|
|
5730
|
-
}).builder.getSpecAsYaml();
|
|
5761
|
+
new OpenAPI({/* ... */}).builder.getSpecAsYaml();
|
|
5731
5762
|
// after
|
|
5732
|
-
new OpenAPI({
|
|
5733
|
-
/* ... */
|
|
5734
|
-
}).getSpecAsYaml();
|
|
5763
|
+
new OpenAPI({/* ... */}).getSpecAsYaml();
|
|
5735
5764
|
```
|
|
5736
5765
|
|
|
5737
5766
|
```ts
|
|
@@ -5756,12 +5785,7 @@ const myResultHandlerV2 = createResultHandler({
|
|
|
5756
5785
|
}),
|
|
5757
5786
|
["mime/type1", "mime/type2"], // optional, default: application/json
|
|
5758
5787
|
),
|
|
5759
|
-
getNegativeResponse: () =>
|
|
5760
|
-
createApiResponse(
|
|
5761
|
-
z.object({
|
|
5762
|
-
/* ... */
|
|
5763
|
-
}),
|
|
5764
|
-
),
|
|
5788
|
+
getNegativeResponse: () => createApiResponse(z.object({/* ... */})),
|
|
5765
5789
|
handler: ({ error, input, output, request, response, logger }) => {
|
|
5766
5790
|
/* ... */
|
|
5767
5791
|
},
|
package/README.md
CHANGED
|
@@ -83,7 +83,7 @@ Therefore, many basic tasks can be achieved faster and easier, in particular:
|
|
|
83
83
|
- All of your endpoints can respond consistently.
|
|
84
84
|
- The expected endpoint input and response types can be exported to the frontend, giving you end-to-end type safety
|
|
85
85
|
so you don't get confused about the field names when you implement the client for your API.
|
|
86
|
-
- You can generate your API documentation in OpenAPI 3.
|
|
86
|
+
- You can generate your API documentation in OpenAPI 3.2 and JSON Schema compatible format.
|
|
87
87
|
|
|
88
88
|
## Contributors
|
|
89
89
|
|
|
@@ -171,7 +171,7 @@ Much can be customized to fit your needs.
|
|
|
171
171
|
- Supports any logger having `info()`, `debug()`, `error()` and `warn()` methods;
|
|
172
172
|
- Built-in console logger with colorful and pretty inspections by default.
|
|
173
173
|
- Generators:
|
|
174
|
-
- Documentation — [OpenAPI 3.
|
|
174
|
+
- Documentation — [OpenAPI 3.2](https://github.com/metadevpro/openapi3-ts) (former Swagger);
|
|
175
175
|
- Client side types — inspired by [zod-to-ts](https://github.com/sachinraja/zod-to-ts).
|
|
176
176
|
- File uploads — [Express-FileUpload](https://github.com/richardgirges/express-fileupload)
|
|
177
177
|
(based on [Busboy](https://github.com/mscdex/busboy)).
|
|
@@ -424,9 +424,9 @@ const resultHandlerWithCleanup = new ResultHandler({
|
|
|
424
424
|
|
|
425
425
|
There are two ways of connecting the native express middlewares depending on their nature and your objective.
|
|
426
426
|
|
|
427
|
-
In case it's
|
|
428
|
-
|
|
429
|
-
|
|
427
|
+
In case it's middleware establishing and serving its own routes, or somehow globally modifying the behavior, use the
|
|
428
|
+
`beforeRouting` or `beforeParsers` hooks. Note that [CORS](#cross-origin-resource-sharing), cookies, compression, and
|
|
429
|
+
body parsing are already available as separate [config options](#set-up-config).
|
|
430
430
|
|
|
431
431
|
```ts
|
|
432
432
|
import { createConfig } from "express-zod-api";
|
|
@@ -647,18 +647,27 @@ const listUsers = defaultEndpointsFactory.build({
|
|
|
647
647
|
## Cross-Origin Resource Sharing
|
|
648
648
|
|
|
649
649
|
You can enable your API for other domains using the corresponding configuration option `cors`. The value is required to
|
|
650
|
-
ensure you explicitly choose the correct setting
|
|
651
|
-
|
|
650
|
+
ensure you explicitly choose the correct setting: `false | true` — disables/enables CORS for any origin, setting
|
|
651
|
+
`Access-Control-Allow-Origin: *` and `Access-Control-Allow-Headers: content-type`. You can also pass standard Express
|
|
652
|
+
middleware for full control, consider using the well-known [cors](https://www.npmjs.com/package/cors) package.
|
|
652
653
|
|
|
653
654
|
```ts
|
|
655
|
+
import cors from "cors";
|
|
654
656
|
import { createConfig } from "express-zod-api";
|
|
655
657
|
|
|
656
|
-
const
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
658
|
+
const configWithCorsPackage = createConfig({
|
|
659
|
+
cors: cors({ origin: "https://example.com" }),
|
|
660
|
+
});
|
|
661
|
+
|
|
662
|
+
const configWithCustomRequestHandler = createConfig({
|
|
663
|
+
cors: (req, res, next) => {
|
|
664
|
+
res.set({
|
|
665
|
+
"Access-Control-Allow-Origin": "https://example.com",
|
|
666
|
+
"Access-Control-Allow-Headers": "content-type",
|
|
667
|
+
"Access-Control-Max-Age": "5000",
|
|
668
|
+
});
|
|
669
|
+
next();
|
|
670
|
+
},
|
|
662
671
|
});
|
|
663
672
|
```
|
|
664
673
|
|
|
@@ -683,9 +692,7 @@ const config = createConfig({
|
|
|
683
692
|
}, // ... cors, logger, etc
|
|
684
693
|
});
|
|
685
694
|
|
|
686
|
-
|
|
687
|
-
// For top level CJS you can wrap you code with (async () => { ... })()
|
|
688
|
-
const { app, servers, logger } = await createServer(config, routing);
|
|
695
|
+
const { app, servers, logger } = createServer(config, routing);
|
|
689
696
|
```
|
|
690
697
|
|
|
691
698
|
Ensure having `@types/node` package installed. At least you need to specify the port (usually it is 443) or UNIX socket,
|
|
@@ -787,6 +794,7 @@ createConfig({
|
|
|
787
794
|
put: ["body", "params"],
|
|
788
795
|
patch: ["body", "params"],
|
|
789
796
|
delete: ["query", "params"],
|
|
797
|
+
query: ["query", "body", "params"],
|
|
790
798
|
}, // ...
|
|
791
799
|
});
|
|
792
800
|
```
|
|
@@ -1124,7 +1132,7 @@ errors yourself. In this regard `attachRouting()` provides you with `notFoundHan
|
|
|
1124
1132
|
to your custom express app.
|
|
1125
1133
|
|
|
1126
1134
|
Besides that, if you're looking to include additional request parsers, or a middleware that establishes its own routes,
|
|
1127
|
-
|
|
1135
|
+
install them on your custom `app` before calling `attachRouting()`.
|
|
1128
1136
|
|
|
1129
1137
|
## Testing endpoints
|
|
1130
1138
|
|
|
@@ -1241,9 +1249,8 @@ import { Documentation } from "express-zod-api";
|
|
|
1241
1249
|
const yamlString = new Documentation({
|
|
1242
1250
|
routing, // the same routing and config that you use to start the server
|
|
1243
1251
|
config,
|
|
1244
|
-
version: "1.2.3",
|
|
1245
|
-
|
|
1246
|
-
serverUrl: "https://example.com",
|
|
1252
|
+
info: { version: "1.2.3", title: "Example API" },
|
|
1253
|
+
server: "https://example.com",
|
|
1247
1254
|
composition: "inline", // optional, or "components" for keeping schemas in a separate dedicated section using refs
|
|
1248
1255
|
// descriptions: { positiveResponse, negativeResponse, requestParameter, requestBody }, // check out these features
|
|
1249
1256
|
}).getSpecAsYaml();
|
package/SECURITY.md
CHANGED
|
@@ -4,10 +4,11 @@
|
|
|
4
4
|
|
|
5
5
|
| Version | Code name | Release | Supported |
|
|
6
6
|
| ------: | :------------ | :------ | :----------------: |
|
|
7
|
+
| 29.x.x | Angie | 08.2026 | :white_check_mark: |
|
|
7
8
|
| 28.x.x | Koko | 05.2026 | :white_check_mark: |
|
|
8
9
|
| 27.x.x | Nikki | 02.2026 | :white_check_mark: |
|
|
9
10
|
| 26.x.x | Lia | 12.2025 | :white_check_mark: |
|
|
10
|
-
| 25.x.x | Sara | 08.2025 |
|
|
11
|
+
| 25.x.x | Sara | 08.2025 | :x: |
|
|
11
12
|
| 24.x.x | Ashley | 06.2025 | :x: |
|
|
12
13
|
| 23.x.x | Sonia | 04.2025 | :x: |
|
|
13
14
|
| 22.x.x | Tai | 01.2025 | :x: |
|