express-zod-routes 1.0.0
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/LICENSE +21 -0
- package/README.md +126 -0
- package/dist/index.cjs +75 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +99 -0
- package/dist/index.d.ts +99 -0
- package/dist/index.js +69 -0
- package/dist/index.js.map +1 -0
- package/package.json +79 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
# express-zod-routes
|
|
2
|
+
|
|
3
|
+
[](https://github.com/adriangrahldev/express-zod-routes/actions/workflows/ci.yml)
|
|
4
|
+
[](https://www.npmjs.com/package/express-zod-routes)
|
|
5
|
+
[](LICENSE)
|
|
6
|
+
|
|
7
|
+
**Express middleware** that validates `body`, `query`, and `params` with **[Zod](https://zod.dev)**. On success, parsed values land on **`req.validated`**. On failure, the client gets a **consistent JSON 400** (or your chosen status) without calling `next`.
|
|
8
|
+
|
|
9
|
+
Built for **MERN / TypeScript** stacks: one source of truth for validation, no duplicate DTO logic.
|
|
10
|
+
|
|
11
|
+
## Install
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npm install express-zod-routes
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Peer dependencies:
|
|
18
|
+
|
|
19
|
+
- `express` `^4.18.0 || ^5.0.0`
|
|
20
|
+
- `zod` `^3.22.0`
|
|
21
|
+
|
|
22
|
+
## Quick start
|
|
23
|
+
|
|
24
|
+
```ts
|
|
25
|
+
import express from 'express';
|
|
26
|
+
import { z } from 'zod';
|
|
27
|
+
import { validate } from 'express-zod-routes';
|
|
28
|
+
|
|
29
|
+
const app = express();
|
|
30
|
+
app.use(express.json());
|
|
31
|
+
|
|
32
|
+
app.post(
|
|
33
|
+
'/users',
|
|
34
|
+
validate({
|
|
35
|
+
body: z.object({
|
|
36
|
+
name: z.string().min(1),
|
|
37
|
+
email: z.string().email(),
|
|
38
|
+
}),
|
|
39
|
+
}),
|
|
40
|
+
(req, res) => {
|
|
41
|
+
// req.validated.body is parsed & typed with InferValidated (see below)
|
|
42
|
+
res.status(201).json(req.validated!.body);
|
|
43
|
+
}
|
|
44
|
+
);
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## API
|
|
48
|
+
|
|
49
|
+
### `validate(schemas)` · `createValidator(schemas)`
|
|
50
|
+
|
|
51
|
+
Same function; `createValidator` is an alias.
|
|
52
|
+
|
|
53
|
+
| Option | Type | Description |
|
|
54
|
+
| -------------- | --------- | ------------------------------------------------- |
|
|
55
|
+
| `body` | `ZodType` | Validates `req.body` (use after `express.json()`) |
|
|
56
|
+
| `query` | `ZodType` | Validates `req.query` (strings → use `z.coerce`) |
|
|
57
|
+
| `params` | `ZodType` | Validates `req.params` |
|
|
58
|
+
| `statusCode` | `number` | Default `400` |
|
|
59
|
+
| `errorMessage` | `string` | Default `"Validation failed"` |
|
|
60
|
+
|
|
61
|
+
At least one of `body`, `query`, or `params` is required.
|
|
62
|
+
|
|
63
|
+
### `req.validated`
|
|
64
|
+
|
|
65
|
+
After the middleware runs successfully, `req.validated` contains only the keys you validated (merged if you stack multiple validators on the same route — usually one per route is enough).
|
|
66
|
+
|
|
67
|
+
Importing the package augments Express’s `Request` so `validated` is recognized by TypeScript.
|
|
68
|
+
|
|
69
|
+
### Typing handlers with `InferValidated`
|
|
70
|
+
|
|
71
|
+
```ts
|
|
72
|
+
import type { Request } from 'express';
|
|
73
|
+
import { validate, type InferValidated } from 'express-zod-routes';
|
|
74
|
+
|
|
75
|
+
const schemas = {
|
|
76
|
+
body: z.object({ email: z.string().email() }),
|
|
77
|
+
} as const;
|
|
78
|
+
|
|
79
|
+
app.post(
|
|
80
|
+
'/',
|
|
81
|
+
validate(schemas),
|
|
82
|
+
(req: Request & { validated: InferValidated<typeof schemas> }, res) => {
|
|
83
|
+
req.validated.body.email;
|
|
84
|
+
res.sendStatus(204);
|
|
85
|
+
}
|
|
86
|
+
);
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### Error response shape
|
|
90
|
+
|
|
91
|
+
```json
|
|
92
|
+
{
|
|
93
|
+
"error": "Validation failed",
|
|
94
|
+
"issues": [{ "path": "email", "message": "Invalid email", "code": "invalid_string" }]
|
|
95
|
+
}
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### `mapZodErrorToResponse(err, message?)`
|
|
99
|
+
|
|
100
|
+
Use in your own error middleware if you reuse the same JSON shape.
|
|
101
|
+
|
|
102
|
+
## Query & params tips
|
|
103
|
+
|
|
104
|
+
- **Query** values are strings (or arrays) from Express. Use `z.coerce.number()`, `z.enum()`, etc.
|
|
105
|
+
- **Params** are strings; use `z.string().uuid()`, regex, or transforms as needed.
|
|
106
|
+
|
|
107
|
+
## Comparison
|
|
108
|
+
|
|
109
|
+
| Approach | express-zod-routes |
|
|
110
|
+
| --------------------- | ------------------------ |
|
|
111
|
+
| Manual `if` + res 400 | Centralized Zod + format |
|
|
112
|
+
| Only body validation | body + query + params |
|
|
113
|
+
|
|
114
|
+
Other great options exist (`express-zod-api`, custom Zod wrappers). This package stays small: one middleware, `req.validated`, stable errors.
|
|
115
|
+
|
|
116
|
+
## Example project
|
|
117
|
+
|
|
118
|
+
See [`examples/basic-express`](examples/basic-express).
|
|
119
|
+
|
|
120
|
+
## Contributing
|
|
121
|
+
|
|
122
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md).
|
|
123
|
+
|
|
124
|
+
## License
|
|
125
|
+
|
|
126
|
+
MIT © see [LICENSE](LICENSE).
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// src/constants.ts
|
|
4
|
+
var DEFAULT_VALIDATION_ERROR_MESSAGE = "Validation failed";
|
|
5
|
+
var DEFAULT_VALIDATION_STATUS = 400;
|
|
6
|
+
|
|
7
|
+
// src/middleware/error-mapper.ts
|
|
8
|
+
function issuePath(issue) {
|
|
9
|
+
return issue.path.map(String).join(".") || "(root)";
|
|
10
|
+
}
|
|
11
|
+
function mapZodErrorToResponse(err, message = DEFAULT_VALIDATION_ERROR_MESSAGE) {
|
|
12
|
+
return {
|
|
13
|
+
error: message,
|
|
14
|
+
issues: err.issues.map((issue) => ({
|
|
15
|
+
path: issuePath(issue),
|
|
16
|
+
message: issue.message,
|
|
17
|
+
code: issue.code
|
|
18
|
+
}))
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// src/middleware/validate.ts
|
|
23
|
+
function parseSegment(schema, raw, errorMessage, statusCode, res) {
|
|
24
|
+
const result = schema.safeParse(raw);
|
|
25
|
+
if (result.success) {
|
|
26
|
+
return { ok: true, data: result.data };
|
|
27
|
+
}
|
|
28
|
+
res.status(statusCode).json(mapZodErrorToResponse(result.error, errorMessage));
|
|
29
|
+
return { ok: false };
|
|
30
|
+
}
|
|
31
|
+
function validate(schemas) {
|
|
32
|
+
const keys = ["body", "query", "params"];
|
|
33
|
+
const hasAny = keys.some((k) => schemas[k] !== void 0);
|
|
34
|
+
if (!hasAny) {
|
|
35
|
+
throw new Error(
|
|
36
|
+
"express-zod-routes: validate() requires at least one of body, query, or params"
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
const statusCode = schemas.statusCode ?? DEFAULT_VALIDATION_STATUS;
|
|
40
|
+
const errorMessage = schemas.errorMessage ?? DEFAULT_VALIDATION_ERROR_MESSAGE;
|
|
41
|
+
return (req, res, next) => {
|
|
42
|
+
try {
|
|
43
|
+
const prev = req.validated ?? {};
|
|
44
|
+
const nextValidated = { ...prev };
|
|
45
|
+
if (schemas.body) {
|
|
46
|
+
const out = parseSegment(schemas.body, req.body, errorMessage, statusCode, res);
|
|
47
|
+
if (!out.ok) return;
|
|
48
|
+
nextValidated.body = out.data;
|
|
49
|
+
}
|
|
50
|
+
if (schemas.query) {
|
|
51
|
+
const out = parseSegment(schemas.query, req.query, errorMessage, statusCode, res);
|
|
52
|
+
if (!out.ok) return;
|
|
53
|
+
nextValidated.query = out.data;
|
|
54
|
+
}
|
|
55
|
+
if (schemas.params) {
|
|
56
|
+
const out = parseSegment(schemas.params, req.params, errorMessage, statusCode, res);
|
|
57
|
+
if (!out.ok) return;
|
|
58
|
+
nextValidated.params = out.data;
|
|
59
|
+
}
|
|
60
|
+
req.validated = nextValidated;
|
|
61
|
+
next();
|
|
62
|
+
} catch (e) {
|
|
63
|
+
next(e);
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
var createValidator = validate;
|
|
68
|
+
|
|
69
|
+
exports.DEFAULT_VALIDATION_ERROR_MESSAGE = DEFAULT_VALIDATION_ERROR_MESSAGE;
|
|
70
|
+
exports.DEFAULT_VALIDATION_STATUS = DEFAULT_VALIDATION_STATUS;
|
|
71
|
+
exports.createValidator = createValidator;
|
|
72
|
+
exports.mapZodErrorToResponse = mapZodErrorToResponse;
|
|
73
|
+
exports.validate = validate;
|
|
74
|
+
//# sourceMappingURL=index.cjs.map
|
|
75
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/constants.ts","../src/middleware/error-mapper.ts","../src/middleware/validate.ts"],"names":[],"mappings":"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","file":"index.cjs","sourcesContent":["/** Default message for validation error responses */\nexport const DEFAULT_VALIDATION_ERROR_MESSAGE = 'Validation failed' as const;\n\n/** Default HTTP status when validation fails */\nexport const DEFAULT_VALIDATION_STATUS = 400 as const;\n","import type { ZodError, ZodIssue } from 'zod';\nimport { DEFAULT_VALIDATION_ERROR_MESSAGE } from '../constants.js';\n\n/** One validation issue in the API response */\nexport interface ValidationIssue {\n /** Dot-separated path (e.g. `user.email`) */\n path: string;\n message: string;\n code: ZodIssue['code'];\n}\n\n/** Stable JSON body for HTTP 400 validation errors */\nexport interface ValidationErrorBody {\n error: string;\n issues: ValidationIssue[];\n}\n\nfunction issuePath(issue: ZodIssue): string {\n return issue.path.map(String).join('.') || '(root)';\n}\n\n/**\n * Maps a Zod error to the package’s standard error payload (for custom error middleware).\n */\nexport function mapZodErrorToResponse(\n err: ZodError,\n message: string = DEFAULT_VALIDATION_ERROR_MESSAGE\n): ValidationErrorBody {\n return {\n error: message,\n issues: err.issues.map((issue) => ({\n path: issuePath(issue),\n message: issue.message,\n code: issue.code,\n })),\n };\n}\n","import type { NextFunction, Request, RequestHandler, Response } from 'express';\nimport type { ZodTypeAny } from 'zod';\nimport { DEFAULT_VALIDATION_ERROR_MESSAGE, DEFAULT_VALIDATION_STATUS } from '../constants.js';\nimport type { ValidationSchemas } from '../types/schemas.js';\nimport { mapZodErrorToResponse } from './error-mapper.js';\n\nexport interface ValidateOptions extends ValidationSchemas {\n /** HTTP status when validation fails (default: 400) */\n statusCode?: number;\n /** Override default error message in JSON body */\n errorMessage?: string;\n}\n\nfunction parseSegment(\n schema: ZodTypeAny,\n raw: unknown,\n errorMessage: string,\n statusCode: number,\n res: Response\n): { ok: true; data: unknown } | { ok: false } {\n const result = schema.safeParse(raw);\n if (result.success) {\n return { ok: true, data: result.data };\n }\n res.status(statusCode).json(mapZodErrorToResponse(result.error, errorMessage));\n return { ok: false };\n}\n\n/**\n * Express middleware: validates `body`, `query`, and/or `params` with Zod.\n * On success, merges results into `req.validated`. On failure, sends JSON 400 and does not call `next`.\n *\n * @example\n * ```ts\n * app.post(\n * '/users',\n * express.json(),\n * validate({ body: z.object({ name: z.string().min(1) }) }),\n * (req, res) => res.json(req.validated!.body)\n * );\n * ```\n */\nexport function validate(schemas: ValidateOptions): RequestHandler {\n const keys = ['body', 'query', 'params'] as const;\n const hasAny = keys.some((k) => schemas[k] !== undefined);\n if (!hasAny) {\n throw new Error(\n 'express-zod-routes: validate() requires at least one of body, query, or params'\n );\n }\n\n const statusCode = schemas.statusCode ?? DEFAULT_VALIDATION_STATUS;\n const errorMessage = schemas.errorMessage ?? DEFAULT_VALIDATION_ERROR_MESSAGE;\n\n return (req: Request, res: Response, next: NextFunction) => {\n try {\n const prev = req.validated ?? {};\n const nextValidated: NonNullable<Request['validated']> = { ...prev };\n\n if (schemas.body) {\n const out = parseSegment(schemas.body, req.body, errorMessage, statusCode, res);\n if (!out.ok) return;\n nextValidated.body = out.data;\n }\n if (schemas.query) {\n const out = parseSegment(schemas.query, req.query, errorMessage, statusCode, res);\n if (!out.ok) return;\n nextValidated.query = out.data;\n }\n if (schemas.params) {\n const out = parseSegment(schemas.params, req.params, errorMessage, statusCode, res);\n if (!out.ok) return;\n nextValidated.params = out.data;\n }\n\n req.validated = nextValidated;\n next();\n } catch (e) {\n next(e);\n }\n };\n}\n\n/** Alias for {@link validate} */\nexport const createValidator = validate;\n"]}
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { RequestHandler } from 'express';
|
|
2
|
+
import { z, ZodIssue, ZodError } from 'zod';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Augments Express `Request` with `validated` when you import `express-zod-routes`.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
declare global {
|
|
9
|
+
namespace Express {
|
|
10
|
+
interface Request {
|
|
11
|
+
/**
|
|
12
|
+
* Parsed values after {@link validate}. Only keys you passed schemas for are set.
|
|
13
|
+
*/
|
|
14
|
+
validated?: {
|
|
15
|
+
body?: unknown;
|
|
16
|
+
query?: unknown;
|
|
17
|
+
params?: unknown;
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** Schemas you can pass to {@link validate} */
|
|
24
|
+
interface ValidationSchemas {
|
|
25
|
+
body?: z.ZodTypeAny;
|
|
26
|
+
query?: z.ZodTypeAny;
|
|
27
|
+
params?: z.ZodTypeAny;
|
|
28
|
+
}
|
|
29
|
+
type Empty = Record<never, never>;
|
|
30
|
+
/**
|
|
31
|
+
* Shape of `req.validated` inferred from the schemas passed to {@link validate}.
|
|
32
|
+
*
|
|
33
|
+
* @example
|
|
34
|
+
* ```ts
|
|
35
|
+
* const schemas = { body: z.object({ email: z.string().email() }) };
|
|
36
|
+
* app.post(
|
|
37
|
+
* '/',
|
|
38
|
+
* validate(schemas),
|
|
39
|
+
* (req: Request & { validated: InferValidated<typeof schemas> }, res) => {
|
|
40
|
+
* req.validated.body.email;
|
|
41
|
+
* }
|
|
42
|
+
* );
|
|
43
|
+
* ```
|
|
44
|
+
*/
|
|
45
|
+
type InferValidated<S extends ValidationSchemas> = (S['body'] extends z.ZodTypeAny ? {
|
|
46
|
+
body: z.infer<NonNullable<S['body']>>;
|
|
47
|
+
} : Empty) & (S['query'] extends z.ZodTypeAny ? {
|
|
48
|
+
query: z.infer<NonNullable<S['query']>>;
|
|
49
|
+
} : Empty) & (S['params'] extends z.ZodTypeAny ? {
|
|
50
|
+
params: z.infer<NonNullable<S['params']>>;
|
|
51
|
+
} : Empty);
|
|
52
|
+
|
|
53
|
+
interface ValidateOptions extends ValidationSchemas {
|
|
54
|
+
/** HTTP status when validation fails (default: 400) */
|
|
55
|
+
statusCode?: number;
|
|
56
|
+
/** Override default error message in JSON body */
|
|
57
|
+
errorMessage?: string;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Express middleware: validates `body`, `query`, and/or `params` with Zod.
|
|
61
|
+
* On success, merges results into `req.validated`. On failure, sends JSON 400 and does not call `next`.
|
|
62
|
+
*
|
|
63
|
+
* @example
|
|
64
|
+
* ```ts
|
|
65
|
+
* app.post(
|
|
66
|
+
* '/users',
|
|
67
|
+
* express.json(),
|
|
68
|
+
* validate({ body: z.object({ name: z.string().min(1) }) }),
|
|
69
|
+
* (req, res) => res.json(req.validated!.body)
|
|
70
|
+
* );
|
|
71
|
+
* ```
|
|
72
|
+
*/
|
|
73
|
+
declare function validate(schemas: ValidateOptions): RequestHandler;
|
|
74
|
+
/** Alias for {@link validate} */
|
|
75
|
+
declare const createValidator: typeof validate;
|
|
76
|
+
|
|
77
|
+
/** One validation issue in the API response */
|
|
78
|
+
interface ValidationIssue {
|
|
79
|
+
/** Dot-separated path (e.g. `user.email`) */
|
|
80
|
+
path: string;
|
|
81
|
+
message: string;
|
|
82
|
+
code: ZodIssue['code'];
|
|
83
|
+
}
|
|
84
|
+
/** Stable JSON body for HTTP 400 validation errors */
|
|
85
|
+
interface ValidationErrorBody {
|
|
86
|
+
error: string;
|
|
87
|
+
issues: ValidationIssue[];
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Maps a Zod error to the package’s standard error payload (for custom error middleware).
|
|
91
|
+
*/
|
|
92
|
+
declare function mapZodErrorToResponse(err: ZodError, message?: string): ValidationErrorBody;
|
|
93
|
+
|
|
94
|
+
/** Default message for validation error responses */
|
|
95
|
+
declare const DEFAULT_VALIDATION_ERROR_MESSAGE: "Validation failed";
|
|
96
|
+
/** Default HTTP status when validation fails */
|
|
97
|
+
declare const DEFAULT_VALIDATION_STATUS: 400;
|
|
98
|
+
|
|
99
|
+
export { DEFAULT_VALIDATION_ERROR_MESSAGE, DEFAULT_VALIDATION_STATUS, type InferValidated, type ValidateOptions, type ValidationErrorBody, type ValidationIssue, type ValidationSchemas, createValidator, mapZodErrorToResponse, validate };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { RequestHandler } from 'express';
|
|
2
|
+
import { z, ZodIssue, ZodError } from 'zod';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Augments Express `Request` with `validated` when you import `express-zod-routes`.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
declare global {
|
|
9
|
+
namespace Express {
|
|
10
|
+
interface Request {
|
|
11
|
+
/**
|
|
12
|
+
* Parsed values after {@link validate}. Only keys you passed schemas for are set.
|
|
13
|
+
*/
|
|
14
|
+
validated?: {
|
|
15
|
+
body?: unknown;
|
|
16
|
+
query?: unknown;
|
|
17
|
+
params?: unknown;
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** Schemas you can pass to {@link validate} */
|
|
24
|
+
interface ValidationSchemas {
|
|
25
|
+
body?: z.ZodTypeAny;
|
|
26
|
+
query?: z.ZodTypeAny;
|
|
27
|
+
params?: z.ZodTypeAny;
|
|
28
|
+
}
|
|
29
|
+
type Empty = Record<never, never>;
|
|
30
|
+
/**
|
|
31
|
+
* Shape of `req.validated` inferred from the schemas passed to {@link validate}.
|
|
32
|
+
*
|
|
33
|
+
* @example
|
|
34
|
+
* ```ts
|
|
35
|
+
* const schemas = { body: z.object({ email: z.string().email() }) };
|
|
36
|
+
* app.post(
|
|
37
|
+
* '/',
|
|
38
|
+
* validate(schemas),
|
|
39
|
+
* (req: Request & { validated: InferValidated<typeof schemas> }, res) => {
|
|
40
|
+
* req.validated.body.email;
|
|
41
|
+
* }
|
|
42
|
+
* );
|
|
43
|
+
* ```
|
|
44
|
+
*/
|
|
45
|
+
type InferValidated<S extends ValidationSchemas> = (S['body'] extends z.ZodTypeAny ? {
|
|
46
|
+
body: z.infer<NonNullable<S['body']>>;
|
|
47
|
+
} : Empty) & (S['query'] extends z.ZodTypeAny ? {
|
|
48
|
+
query: z.infer<NonNullable<S['query']>>;
|
|
49
|
+
} : Empty) & (S['params'] extends z.ZodTypeAny ? {
|
|
50
|
+
params: z.infer<NonNullable<S['params']>>;
|
|
51
|
+
} : Empty);
|
|
52
|
+
|
|
53
|
+
interface ValidateOptions extends ValidationSchemas {
|
|
54
|
+
/** HTTP status when validation fails (default: 400) */
|
|
55
|
+
statusCode?: number;
|
|
56
|
+
/** Override default error message in JSON body */
|
|
57
|
+
errorMessage?: string;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Express middleware: validates `body`, `query`, and/or `params` with Zod.
|
|
61
|
+
* On success, merges results into `req.validated`. On failure, sends JSON 400 and does not call `next`.
|
|
62
|
+
*
|
|
63
|
+
* @example
|
|
64
|
+
* ```ts
|
|
65
|
+
* app.post(
|
|
66
|
+
* '/users',
|
|
67
|
+
* express.json(),
|
|
68
|
+
* validate({ body: z.object({ name: z.string().min(1) }) }),
|
|
69
|
+
* (req, res) => res.json(req.validated!.body)
|
|
70
|
+
* );
|
|
71
|
+
* ```
|
|
72
|
+
*/
|
|
73
|
+
declare function validate(schemas: ValidateOptions): RequestHandler;
|
|
74
|
+
/** Alias for {@link validate} */
|
|
75
|
+
declare const createValidator: typeof validate;
|
|
76
|
+
|
|
77
|
+
/** One validation issue in the API response */
|
|
78
|
+
interface ValidationIssue {
|
|
79
|
+
/** Dot-separated path (e.g. `user.email`) */
|
|
80
|
+
path: string;
|
|
81
|
+
message: string;
|
|
82
|
+
code: ZodIssue['code'];
|
|
83
|
+
}
|
|
84
|
+
/** Stable JSON body for HTTP 400 validation errors */
|
|
85
|
+
interface ValidationErrorBody {
|
|
86
|
+
error: string;
|
|
87
|
+
issues: ValidationIssue[];
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Maps a Zod error to the package’s standard error payload (for custom error middleware).
|
|
91
|
+
*/
|
|
92
|
+
declare function mapZodErrorToResponse(err: ZodError, message?: string): ValidationErrorBody;
|
|
93
|
+
|
|
94
|
+
/** Default message for validation error responses */
|
|
95
|
+
declare const DEFAULT_VALIDATION_ERROR_MESSAGE: "Validation failed";
|
|
96
|
+
/** Default HTTP status when validation fails */
|
|
97
|
+
declare const DEFAULT_VALIDATION_STATUS: 400;
|
|
98
|
+
|
|
99
|
+
export { DEFAULT_VALIDATION_ERROR_MESSAGE, DEFAULT_VALIDATION_STATUS, type InferValidated, type ValidateOptions, type ValidationErrorBody, type ValidationIssue, type ValidationSchemas, createValidator, mapZodErrorToResponse, validate };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
// src/constants.ts
|
|
2
|
+
var DEFAULT_VALIDATION_ERROR_MESSAGE = "Validation failed";
|
|
3
|
+
var DEFAULT_VALIDATION_STATUS = 400;
|
|
4
|
+
|
|
5
|
+
// src/middleware/error-mapper.ts
|
|
6
|
+
function issuePath(issue) {
|
|
7
|
+
return issue.path.map(String).join(".") || "(root)";
|
|
8
|
+
}
|
|
9
|
+
function mapZodErrorToResponse(err, message = DEFAULT_VALIDATION_ERROR_MESSAGE) {
|
|
10
|
+
return {
|
|
11
|
+
error: message,
|
|
12
|
+
issues: err.issues.map((issue) => ({
|
|
13
|
+
path: issuePath(issue),
|
|
14
|
+
message: issue.message,
|
|
15
|
+
code: issue.code
|
|
16
|
+
}))
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// src/middleware/validate.ts
|
|
21
|
+
function parseSegment(schema, raw, errorMessage, statusCode, res) {
|
|
22
|
+
const result = schema.safeParse(raw);
|
|
23
|
+
if (result.success) {
|
|
24
|
+
return { ok: true, data: result.data };
|
|
25
|
+
}
|
|
26
|
+
res.status(statusCode).json(mapZodErrorToResponse(result.error, errorMessage));
|
|
27
|
+
return { ok: false };
|
|
28
|
+
}
|
|
29
|
+
function validate(schemas) {
|
|
30
|
+
const keys = ["body", "query", "params"];
|
|
31
|
+
const hasAny = keys.some((k) => schemas[k] !== void 0);
|
|
32
|
+
if (!hasAny) {
|
|
33
|
+
throw new Error(
|
|
34
|
+
"express-zod-routes: validate() requires at least one of body, query, or params"
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
const statusCode = schemas.statusCode ?? DEFAULT_VALIDATION_STATUS;
|
|
38
|
+
const errorMessage = schemas.errorMessage ?? DEFAULT_VALIDATION_ERROR_MESSAGE;
|
|
39
|
+
return (req, res, next) => {
|
|
40
|
+
try {
|
|
41
|
+
const prev = req.validated ?? {};
|
|
42
|
+
const nextValidated = { ...prev };
|
|
43
|
+
if (schemas.body) {
|
|
44
|
+
const out = parseSegment(schemas.body, req.body, errorMessage, statusCode, res);
|
|
45
|
+
if (!out.ok) return;
|
|
46
|
+
nextValidated.body = out.data;
|
|
47
|
+
}
|
|
48
|
+
if (schemas.query) {
|
|
49
|
+
const out = parseSegment(schemas.query, req.query, errorMessage, statusCode, res);
|
|
50
|
+
if (!out.ok) return;
|
|
51
|
+
nextValidated.query = out.data;
|
|
52
|
+
}
|
|
53
|
+
if (schemas.params) {
|
|
54
|
+
const out = parseSegment(schemas.params, req.params, errorMessage, statusCode, res);
|
|
55
|
+
if (!out.ok) return;
|
|
56
|
+
nextValidated.params = out.data;
|
|
57
|
+
}
|
|
58
|
+
req.validated = nextValidated;
|
|
59
|
+
next();
|
|
60
|
+
} catch (e) {
|
|
61
|
+
next(e);
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
var createValidator = validate;
|
|
66
|
+
|
|
67
|
+
export { DEFAULT_VALIDATION_ERROR_MESSAGE, DEFAULT_VALIDATION_STATUS, createValidator, mapZodErrorToResponse, validate };
|
|
68
|
+
//# sourceMappingURL=index.js.map
|
|
69
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/constants.ts","../src/middleware/error-mapper.ts","../src/middleware/validate.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/** Default message for validation error responses */\nexport const DEFAULT_VALIDATION_ERROR_MESSAGE = 'Validation failed' as const;\n\n/** Default HTTP status when validation fails */\nexport const DEFAULT_VALIDATION_STATUS = 400 as const;\n","import type { ZodError, ZodIssue } from 'zod';\nimport { DEFAULT_VALIDATION_ERROR_MESSAGE } from '../constants.js';\n\n/** One validation issue in the API response */\nexport interface ValidationIssue {\n /** Dot-separated path (e.g. `user.email`) */\n path: string;\n message: string;\n code: ZodIssue['code'];\n}\n\n/** Stable JSON body for HTTP 400 validation errors */\nexport interface ValidationErrorBody {\n error: string;\n issues: ValidationIssue[];\n}\n\nfunction issuePath(issue: ZodIssue): string {\n return issue.path.map(String).join('.') || '(root)';\n}\n\n/**\n * Maps a Zod error to the package’s standard error payload (for custom error middleware).\n */\nexport function mapZodErrorToResponse(\n err: ZodError,\n message: string = DEFAULT_VALIDATION_ERROR_MESSAGE\n): ValidationErrorBody {\n return {\n error: message,\n issues: err.issues.map((issue) => ({\n path: issuePath(issue),\n message: issue.message,\n code: issue.code,\n })),\n };\n}\n","import type { NextFunction, Request, RequestHandler, Response } from 'express';\nimport type { ZodTypeAny } from 'zod';\nimport { DEFAULT_VALIDATION_ERROR_MESSAGE, DEFAULT_VALIDATION_STATUS } from '../constants.js';\nimport type { ValidationSchemas } from '../types/schemas.js';\nimport { mapZodErrorToResponse } from './error-mapper.js';\n\nexport interface ValidateOptions extends ValidationSchemas {\n /** HTTP status when validation fails (default: 400) */\n statusCode?: number;\n /** Override default error message in JSON body */\n errorMessage?: string;\n}\n\nfunction parseSegment(\n schema: ZodTypeAny,\n raw: unknown,\n errorMessage: string,\n statusCode: number,\n res: Response\n): { ok: true; data: unknown } | { ok: false } {\n const result = schema.safeParse(raw);\n if (result.success) {\n return { ok: true, data: result.data };\n }\n res.status(statusCode).json(mapZodErrorToResponse(result.error, errorMessage));\n return { ok: false };\n}\n\n/**\n * Express middleware: validates `body`, `query`, and/or `params` with Zod.\n * On success, merges results into `req.validated`. On failure, sends JSON 400 and does not call `next`.\n *\n * @example\n * ```ts\n * app.post(\n * '/users',\n * express.json(),\n * validate({ body: z.object({ name: z.string().min(1) }) }),\n * (req, res) => res.json(req.validated!.body)\n * );\n * ```\n */\nexport function validate(schemas: ValidateOptions): RequestHandler {\n const keys = ['body', 'query', 'params'] as const;\n const hasAny = keys.some((k) => schemas[k] !== undefined);\n if (!hasAny) {\n throw new Error(\n 'express-zod-routes: validate() requires at least one of body, query, or params'\n );\n }\n\n const statusCode = schemas.statusCode ?? DEFAULT_VALIDATION_STATUS;\n const errorMessage = schemas.errorMessage ?? DEFAULT_VALIDATION_ERROR_MESSAGE;\n\n return (req: Request, res: Response, next: NextFunction) => {\n try {\n const prev = req.validated ?? {};\n const nextValidated: NonNullable<Request['validated']> = { ...prev };\n\n if (schemas.body) {\n const out = parseSegment(schemas.body, req.body, errorMessage, statusCode, res);\n if (!out.ok) return;\n nextValidated.body = out.data;\n }\n if (schemas.query) {\n const out = parseSegment(schemas.query, req.query, errorMessage, statusCode, res);\n if (!out.ok) return;\n nextValidated.query = out.data;\n }\n if (schemas.params) {\n const out = parseSegment(schemas.params, req.params, errorMessage, statusCode, res);\n if (!out.ok) return;\n nextValidated.params = out.data;\n }\n\n req.validated = nextValidated;\n next();\n } catch (e) {\n next(e);\n }\n };\n}\n\n/** Alias for {@link validate} */\nexport const createValidator = validate;\n"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "express-zod-routes",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Express middleware to validate body, query, and params with Zod — typed req.validated and consistent 400 errors",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.cjs",
|
|
7
|
+
"module": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"import": {
|
|
12
|
+
"types": "./dist/index.d.ts",
|
|
13
|
+
"default": "./dist/index.js"
|
|
14
|
+
},
|
|
15
|
+
"require": {
|
|
16
|
+
"types": "./dist/index.d.ts",
|
|
17
|
+
"default": "./dist/index.cjs"
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
"files": [
|
|
22
|
+
"dist",
|
|
23
|
+
"README.md",
|
|
24
|
+
"LICENSE"
|
|
25
|
+
],
|
|
26
|
+
"sideEffects": [
|
|
27
|
+
"./src/global-augment.ts"
|
|
28
|
+
],
|
|
29
|
+
"scripts": {
|
|
30
|
+
"build": "tsup",
|
|
31
|
+
"test": "vitest run",
|
|
32
|
+
"test:watch": "vitest",
|
|
33
|
+
"lint": "eslint .",
|
|
34
|
+
"format": "prettier --write .",
|
|
35
|
+
"format:check": "prettier --check .",
|
|
36
|
+
"typecheck": "tsc --noEmit",
|
|
37
|
+
"prepublishOnly": "npm run build && npm run test"
|
|
38
|
+
},
|
|
39
|
+
"keywords": [
|
|
40
|
+
"express",
|
|
41
|
+
"zod",
|
|
42
|
+
"validation",
|
|
43
|
+
"middleware",
|
|
44
|
+
"typescript",
|
|
45
|
+
"mern"
|
|
46
|
+
],
|
|
47
|
+
"author": "",
|
|
48
|
+
"license": "MIT",
|
|
49
|
+
"repository": {
|
|
50
|
+
"type": "git",
|
|
51
|
+
"url": "git@github.com:adriangrahldev/express-zod-routes.git"
|
|
52
|
+
},
|
|
53
|
+
"bugs": {
|
|
54
|
+
"url": "https://github.com/adriangrahldev/express-zod-routes/issues"
|
|
55
|
+
},
|
|
56
|
+
"homepage": "https://github.com/adriangrahldev/express-zod-routes#readme",
|
|
57
|
+
"engines": {
|
|
58
|
+
"node": ">=18"
|
|
59
|
+
},
|
|
60
|
+
"peerDependencies": {
|
|
61
|
+
"express": "^4.18.0 || ^5.0.0",
|
|
62
|
+
"zod": "^3.22.0"
|
|
63
|
+
},
|
|
64
|
+
"devDependencies": {
|
|
65
|
+
"@types/express": "^5.0.0",
|
|
66
|
+
"@types/node": "^22.10.0",
|
|
67
|
+
"@types/supertest": "^6.0.2",
|
|
68
|
+
"@eslint/js": "^9.16.0",
|
|
69
|
+
"eslint": "^9.16.0",
|
|
70
|
+
"express": "^4.21.0",
|
|
71
|
+
"prettier": "^3.4.2",
|
|
72
|
+
"supertest": "^7.0.0",
|
|
73
|
+
"tsup": "^8.3.5",
|
|
74
|
+
"typescript": "^5.7.2",
|
|
75
|
+
"typescript-eslint": "^8.18.0",
|
|
76
|
+
"vitest": "^2.1.8",
|
|
77
|
+
"zod": "^3.24.1"
|
|
78
|
+
}
|
|
79
|
+
}
|