schema-dsl 2.0.0 → 2.0.1

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.
Files changed (145) hide show
  1. package/CHANGELOG.md +130 -113
  2. package/LICENSE +21 -21
  3. package/README.md +628 -628
  4. package/dist/{DslBuilder-DkLaOo9Q.d.ts → DslBuilder-BIgQOAXp.d.ts} +2 -0
  5. package/dist/{DslBuilder-DQDN0ZxZ.d.cts → DslBuilder-CjHTucNQ.d.cts} +2 -0
  6. package/dist/{Validator-hFWKGxir.d.ts → Validator-CllRdrY0.d.ts} +1 -1
  7. package/dist/{Validator-C7GsVQOH.d.cts → Validator-D6okG9tr.d.cts} +1 -1
  8. package/dist/index.cjs +75 -29
  9. package/dist/index.d.cts +10 -4
  10. package/dist/index.d.ts +10 -4
  11. package/dist/index.js +75 -29
  12. package/dist/plugins/custom-format.cjs +33 -17
  13. package/dist/plugins/custom-format.d.cts +1 -1
  14. package/dist/plugins/custom-format.d.ts +1 -1
  15. package/dist/plugins/custom-format.js +33 -17
  16. package/dist/plugins/custom-type-example.cjs +33 -17
  17. package/dist/plugins/custom-type-example.d.cts +1 -1
  18. package/dist/plugins/custom-type-example.d.ts +1 -1
  19. package/dist/plugins/custom-type-example.js +33 -17
  20. package/dist/plugins/custom-validator.cjs +0 -2
  21. package/dist/plugins/custom-validator.d.cts +1 -1
  22. package/dist/plugins/custom-validator.d.ts +1 -1
  23. package/dist/plugins/custom-validator.js +0 -2
  24. package/docs/FEATURE-INDEX.md +553 -553
  25. package/docs/add-custom-locale.md +496 -496
  26. package/docs/add-keyword.md +24 -24
  27. package/docs/api-reference.md +1047 -1047
  28. package/docs/api.md +13 -13
  29. package/docs/best-practices-project-structure.md +417 -417
  30. package/docs/best-practices.md +712 -712
  31. package/docs/cache-manager.md +344 -344
  32. package/docs/compile.md +45 -45
  33. package/docs/conditional-api.md +1307 -1307
  34. package/docs/custom-extensions-guide.md +339 -339
  35. package/docs/design-philosophy.md +606 -606
  36. package/docs/doc-index.md +324 -324
  37. package/docs/dsl-syntax.md +714 -714
  38. package/docs/dynamic-locale.md +608 -608
  39. package/docs/enum.md +482 -482
  40. package/docs/error-handling.md +1975 -1975
  41. package/docs/export-guide.md +501 -501
  42. package/docs/export-limitations.md +567 -567
  43. package/docs/faq.md +596 -596
  44. package/docs/frontend-i18n-guide.md +307 -307
  45. package/docs/i18n-user-guide.md +487 -487
  46. package/docs/i18n.md +476 -476
  47. package/docs/index.md +48 -48
  48. package/docs/json-schema-basics.md +40 -40
  49. package/docs/label-vs-description.md +271 -271
  50. package/docs/markdown-exporter.md +406 -406
  51. package/docs/mongodb-exporter.md +302 -302
  52. package/docs/multi-language.md +26 -26
  53. package/docs/multi-type-support.md +322 -322
  54. package/docs/mysql-exporter.md +280 -280
  55. package/docs/number-operators.md +449 -449
  56. package/docs/optional-marker-guide.md +326 -326
  57. package/docs/performance-guide.md +49 -49
  58. package/docs/plugin-system.md +381 -381
  59. package/docs/plugin-type-registration.md +34 -34
  60. package/docs/postgresql-exporter.md +311 -311
  61. package/docs/public/favicon.svg +4 -4
  62. package/docs/quick-start.md +435 -435
  63. package/docs/runtime-locale-support.md +532 -532
  64. package/docs/schema-helper.md +345 -345
  65. package/docs/schema-utils-advanced-issues.md +23 -23
  66. package/docs/schema-utils-best-practices.md +20 -20
  67. package/docs/schema-utils-chaining.md +150 -150
  68. package/docs/schema-utils.md +524 -524
  69. package/docs/security-checklist.md +20 -20
  70. package/docs/string-extensions.md +488 -488
  71. package/docs/troubleshooting.md +486 -486
  72. package/docs/type-converter.md +310 -310
  73. package/docs/type-reference.md +242 -242
  74. package/docs/typescript-guide.md +584 -584
  75. package/docs/union-type-guide.md +157 -157
  76. package/docs/union-types.md +284 -284
  77. package/docs/validate-async.md +491 -491
  78. package/docs/validate-batch.md +49 -49
  79. package/docs/validate-dsl-object-support.md +578 -578
  80. package/docs/validate.md +506 -506
  81. package/docs/validation-guide.md +502 -502
  82. package/docs/validator.md +39 -39
  83. package/package.json +131 -131
  84. package/plugins/custom-format.cjs +8 -8
  85. package/plugins/custom-type-example.cjs +8 -8
  86. package/plugins/custom-validator.cjs +8 -8
  87. package/src/adapters/DslAdapter.ts +111 -111
  88. package/src/adapters/index.ts +1 -1
  89. package/src/config/constants.ts +83 -83
  90. package/src/config/index.ts +2 -2
  91. package/src/config/patterns.ts +77 -77
  92. package/src/core/CacheManager.ts +169 -159
  93. package/src/core/ConditionalBuilder.ts +382 -382
  94. package/src/core/ConditionalRuntime.ts +27 -27
  95. package/src/core/ConditionalValidator.ts +254 -254
  96. package/src/core/DslBuilder.ts +687 -677
  97. package/src/core/ErrorCodes.ts +38 -38
  98. package/src/core/ErrorFormatter.ts +271 -271
  99. package/src/core/JSONSchemaCore.ts +65 -65
  100. package/src/core/Locale.ts +187 -187
  101. package/src/core/MessageTemplate.ts +42 -42
  102. package/src/core/ObjectDslBuilder.ts +64 -64
  103. package/src/core/PluginManager.ts +326 -326
  104. package/src/core/StringExtensions.ts +140 -140
  105. package/src/core/TemplateEngine.ts +44 -44
  106. package/src/core/Validator.ts +448 -448
  107. package/src/errors/I18nError.ts +159 -159
  108. package/src/errors/ValidationError.ts +105 -105
  109. package/src/exporters/BaseExporter.ts +60 -60
  110. package/src/exporters/MarkdownExporter.ts +305 -305
  111. package/src/exporters/MongoDBExporter.ts +126 -126
  112. package/src/exporters/MySQLExporter.ts +156 -155
  113. package/src/exporters/PostgreSQLExporter.ts +222 -222
  114. package/src/exporters/index.ts +18 -18
  115. package/src/index.ts +651 -633
  116. package/src/locales/en-US.ts +160 -160
  117. package/src/locales/es-ES.ts +160 -160
  118. package/src/locales/fr-FR.ts +160 -160
  119. package/src/locales/index.ts +103 -103
  120. package/src/locales/ja-JP.ts +160 -160
  121. package/src/locales/types.ts +156 -156
  122. package/src/locales/zh-CN.ts +160 -160
  123. package/src/parser/ConstraintParser.ts +101 -101
  124. package/src/parser/DslParser.ts +470 -470
  125. package/src/parser/SchemaCompiler.ts +66 -66
  126. package/src/parser/TypeRegistry.ts +250 -250
  127. package/src/parser/index.ts +6 -6
  128. package/src/plugins/custom-format.ts +124 -126
  129. package/src/plugins/custom-type-example.ts +106 -108
  130. package/src/plugins/custom-validator.ts +138 -140
  131. package/src/types/conditional.ts +28 -28
  132. package/src/types/config.ts +59 -59
  133. package/src/types/dsl.ts +131 -131
  134. package/src/types/error.ts +60 -60
  135. package/src/types/index.ts +17 -17
  136. package/src/types/infer.ts +127 -127
  137. package/src/types/plugin.ts +58 -58
  138. package/src/types/safe-regex.d.ts +9 -9
  139. package/src/types/schema.ts +66 -66
  140. package/src/types/validate.ts +71 -71
  141. package/src/utils/SchemaHelper.ts +196 -196
  142. package/src/utils/SchemaUtils.ts +365 -346
  143. package/src/utils/TypeConverter.ts +215 -215
  144. package/src/utils/index.ts +10 -10
  145. package/src/validators/CustomKeywords.ts +477 -477
@@ -1,417 +1,417 @@
1
- # Schema-DSL 项目最佳实践示例
2
-
3
- ## 推荐的项目结构
4
-
5
- ```text
6
- your-project/
7
- ├── schemas/ # ✅ 所有 schema 定义(项目启动时加载)
8
- │ ├── index.js # 统一导出
9
- │ ├── user.js # 用户相关 schema
10
- │ ├── order.js # 订单相关 schema
11
- │ └── product.js # 产品相关 schema
12
- ├── routes/
13
- │ ├── user.js # 用户路由(使用 schemas/user.js)
14
- │ ├── order.js # 订单路由(使用 schemas/order.js)
15
- │ └── product.js # 产品路由(使用 schemas/product.js)
16
- └── app.js # 主应用入口
17
- ```
18
-
19
- ---
20
-
21
- ## 完整示例代码
22
-
23
- ### 1. 定义 Schema(schemas/user.js)
24
-
25
- ```javascript
26
- const { dsl } = require('schema-dsl');
27
-
28
- /**
29
- * 用户相关的所有 schema
30
- *
31
- * ✅ 在项目启动时转换一次,后续直接复用
32
- * ✅ 避免每次请求都重复转换
33
- */
34
- const userSchemas = {
35
- // 注册 schema
36
- register: dsl({
37
- username: dsl('string:3-32!')
38
- .pattern(/^[a-zA-Z0-9_]+$/)
39
- .label('用户名')
40
- .messages({
41
- 'string.pattern': '用户名只能包含字母、数字和下划线',
42
- 'string.min': '用户名至少需要3个字符',
43
- 'string.max': '用户名最多32个字符'
44
- }),
45
-
46
- email: dsl('email!')
47
- .label('邮箱')
48
- .messages({
49
- 'string.email': '请输入有效的邮箱地址'
50
- }),
51
-
52
- password: dsl('string!').password('strong')
53
- .label('密码')
54
- .messages({
55
- 'string.password': '密码必须包含大小写字母、数字和特殊字符'
56
- }),
57
-
58
- age: 'number:18-120',
59
-
60
- phone: dsl('phone')
61
- .label('手机号')
62
- .messages({
63
- 'string.phone': '请输入有效的手机号'
64
- })
65
- }),
66
-
67
- // 登录 schema
68
- login: dsl({
69
- username: 'string!',
70
- password: 'string!'
71
- }),
72
-
73
- // 更新个人资料 schema
74
- updateProfile: dsl({
75
- nickname: 'string:2-20',
76
- avatar: 'url',
77
- bio: 'string:0-500',
78
- birthday: 'date',
79
- gender: 'male|female|other'
80
- }),
81
-
82
- // 修改密码 schema
83
- changePassword: dsl({
84
- oldPassword: 'string!',
85
- newPassword: dsl('string!').password('strong')
86
- })
87
- };
88
-
89
- module.exports = userSchemas;
90
- ```
91
-
92
- ### 2. 定义 Schema(schemas/order.js)
93
-
94
- ```javascript
95
- const { dsl } = require('schema-dsl');
96
-
97
- const orderSchemas = {
98
- // 创建订单
99
- create: dsl({
100
- items: 'array:1-100<object>!',
101
- shippingAddress: dsl({
102
- name: 'string:2-50!',
103
- phone: 'phone!',
104
- address: 'string:10-200!',
105
- zipCode: 'string:6'
106
- }),
107
- paymentMethod: 'alipay|wechat|card!',
108
- couponCode: 'string:6-20'
109
- }),
110
-
111
- // 更新订单状态
112
- updateStatus: dsl({
113
- status: 'pending|paid|shipped|completed|cancelled!',
114
- note: 'string:0-500'
115
- })
116
- };
117
-
118
- module.exports = orderSchemas;
119
- ```
120
-
121
- ### 3. 统一导出(schemas/index.js)
122
-
123
- ```javascript
124
- /**
125
- * 统一导出所有 schema
126
- *
127
- * 使用方式:
128
- * const schemas = require('./schemas');
129
- * const result = validate(schemas.user.register, data);
130
- */
131
- module.exports = {
132
- user: require('./user'),
133
- order: require('./order'),
134
- product: require('./product')
135
- };
136
- ```
137
-
138
- ### 4. 在路由中使用(routes/user.js)
139
-
140
- ```javascript
141
- const express = require('express');
142
- const router = express.Router();
143
- const { validate } = require('schema-dsl');
144
- const userSchemas = require('../schemas/user');
145
-
146
- /**
147
- * 用户注册
148
- *
149
- * ✅ 使用预定义的 schema,不再重复转换
150
- */
151
- router.post('/register', async (req, res) => {
152
- // ✅ 直接使用,性能最优
153
- const result = validate(userSchemas.register, req.body);
154
-
155
- if (!result.valid) {
156
- return res.status(400).json({
157
- code: 'VALIDATION_ERROR',
158
- message: '数据验证失败',
159
- errors: result.errors
160
- });
161
- }
162
-
163
- // 处理注册逻辑
164
- try {
165
- const user = await createUser(result.data);
166
- res.status(201).json({
167
- code: 'SUCCESS',
168
- data: user
169
- });
170
- } catch (error) {
171
- res.status(500).json({
172
- code: 'SERVER_ERROR',
173
- message: error.message
174
- });
175
- }
176
- });
177
-
178
- /**
179
- * 用户登录
180
- */
181
- router.post('/login', async (req, res) => {
182
- // ✅ 直接使用
183
- const result = validate(userSchemas.login, req.body);
184
-
185
- if (!result.valid) {
186
- return res.status(400).json({
187
- code: 'VALIDATION_ERROR',
188
- errors: result.errors
189
- });
190
- }
191
-
192
- // 处理登录逻辑
193
- // ...
194
- });
195
-
196
- /**
197
- * 更新个人资料
198
- */
199
- router.put('/profile', authenticate, async (req, res) => {
200
- // ✅ 直接使用
201
- const result = validate(userSchemas.updateProfile, req.body);
202
-
203
- if (!result.valid) {
204
- return res.status(400).json({
205
- code: 'VALIDATION_ERROR',
206
- errors: result.errors
207
- });
208
- }
209
-
210
- // 处理更新逻辑
211
- // ...
212
- });
213
-
214
- module.exports = router;
215
- ```
216
-
217
- ### 5. 主应用入口(app.js)
218
-
219
- ```javascript
220
- const express = require('express');
221
- const app = express();
222
-
223
- // ✅ 在应用启动时加载所有 schema(只转换一次)
224
- const schemas = require('./schemas');
225
- console.log('✅ Schemas loaded:', Object.keys(schemas));
226
-
227
- // 中间件
228
- app.use(express.json());
229
-
230
- // 路由
231
- app.use('/api/user', require('./routes/user'));
232
- app.use('/api/order', require('./routes/order'));
233
- app.use('/api/product', require('./routes/product'));
234
-
235
- // 启动服务
236
- const PORT = process.env.PORT || 3000;
237
- app.listen(PORT, () => {
238
- console.log(`✅ Server started on port ${PORT}`);
239
- console.log('✅ All schemas are loaded and ready to validate');
240
- });
241
-
242
- module.exports = app;
243
- ```
244
-
245
- ---
246
-
247
- ## 性能对比
248
-
249
- ### ❌ 不推荐:每次请求都转换
250
-
251
- ```javascript
252
- // ❌ 错误示例
253
- router.post('/register', (req, res) => {
254
- const result = validate(
255
- { // ❌ 每次请求都转换
256
- username: 'string:3-32!',
257
- email: 'email!',
258
- password: dsl('string!').password('strong')
259
- },
260
- req.body
261
- );
262
- // ...
263
- });
264
- ```
265
-
266
- **性能问题**:
267
- - ❌ 每次请求都执行 DSL → JSON Schema 转换
268
- - ❌ 1000 次请求 = 1000 次转换
269
- - ❌ 高并发时性能损失明显
270
-
271
- ### ✅ 推荐:项目启动时转换
272
-
273
- ```javascript
274
- // ✅ 正确示例
275
- const userSchemas = require('../schemas/user'); // ✅ 启动时加载
276
-
277
- router.post('/register', (req, res) => {
278
- const result = validate(
279
- userSchemas.register, // ✅ 直接使用
280
- req.body
281
- );
282
- // ...
283
- });
284
- ```
285
-
286
- **性能优势**:
287
- - ✅ 启动时转换 1 次
288
- - ✅ 1000 次请求 = 0 次转换
289
- - ✅ 高并发时性能最优
290
-
291
- ---
292
-
293
- ## 使用场景总结
294
-
295
- | 场景 | 推荐方式 | 代码示例 | 原因 |
296
- |------|---------|---------|------|
297
- | **生产环境 API** | ✅ 项目启动时配置 | `const schemas = require('./schemas')` | 避免每次请求都转换 |
298
- | **高并发服务** | ✅ 项目启动时配置 | 同上 | 3-5% 的性能损失会被放大 |
299
- | **微服务** | ✅ 项目启动时配置 | 同上 | 保证响应时间稳定 |
300
- | **单次脚本** | ✅ 直接用 DSL 对象(当前版便捷函数支持) | `validate({ email: 'email!' }, data)` | 只执行一次,性能影响可忽略 |
301
- | **原型开发** | ✅ 直接用 DSL 对象(当前版便捷函数支持) | 同上 | 快速迭代,无需在意性能 |
302
- | **测试代码** | ✅ 直接用 DSL 对象(当前版便捷函数支持) | 同上 | 简洁清晰,易于维护 |
303
-
304
- ---
305
-
306
- ## 常见错误
307
-
308
- ### ❌ 错误1:在路由文件中定义 schema
309
-
310
- ```javascript
311
- // ❌ 不推荐
312
- router.post('/register', (req, res) => {
313
- const schema = dsl({ // ❌ 每次请求都创建
314
- username: 'string:3-32!',
315
- email: 'email!'
316
- });
317
-
318
- const result = validate(schema, req.body);
319
- // ...
320
- });
321
- ```
322
-
323
- **问题**:每次请求都创建新的 schema 对象,浪费性能。
324
-
325
- ### ❌ 错误2:在函数内部定义 schema
326
-
327
- ```javascript
328
- // ❌ 不推荐
329
- function validateUser(data) {
330
- const schema = dsl({ // ❌ 每次调用都创建
331
- username: 'string:3-32!',
332
- email: 'email!'
333
- });
334
-
335
- return validate(schema, data);
336
- }
337
- ```
338
-
339
- **问题**:每次调用函数都创建新的 schema,应该提到函数外部。
340
-
341
- ### ✅ 正确:在模块顶部定义
342
-
343
- ```javascript
344
- // ✅ 推荐:模块加载时创建一次
345
- const userSchema = dsl({
346
- username: 'string:3-32!',
347
- email: 'email!'
348
- });
349
-
350
- router.post('/register', (req, res) => {
351
- const result = validate(userSchema, req.body); // ✅ 直接使用
352
- // ...
353
- });
354
- ```
355
-
356
- ---
357
-
358
- ## TypeScript 支持
359
-
360
- ```typescript
361
- // schemas/user.ts
362
- import { dsl } from 'schema-dsl';
363
-
364
- export const userSchemas = {
365
- register: dsl({
366
- username: dsl('string:3-32!')
367
- .pattern(/^[a-zA-Z0-9_]+$/)
368
- .error({ pattern: '只能包含字母、数字和下划线' }),
369
- email: 'email!',
370
- password: dsl('string:8-64!')
371
- .pattern(/^(?=.*[A-Za-z])(?=.*\d).{8,}$/)
372
- .error({ pattern: '密码至少 8 位且必须包含字母和数字' }),
373
- age: 'number:18-120'
374
- }),
375
-
376
- login: dsl({
377
- username: 'string!',
378
- password: 'string!'
379
- })
380
- };
381
-
382
- // routes/user.ts
383
- import { validate } from 'schema-dsl';
384
- import { userSchemas } from '../schemas/user';
385
-
386
- router.post('/register', (req, res) => {
387
- const result = validate(userSchemas.register, req.body);
388
- // ...
389
- });
390
- ```
391
-
392
- ---
393
-
394
- ## 总结
395
-
396
- **✅ 最佳实践**:
397
- 1. 在单独的 `schemas/` 目录定义所有 schema
398
- 2. 项目启动时加载,转换一次
399
- 3. 路由中直接使用,不再转换
400
- 4. 适合生产环境和高并发场景
401
-
402
- **✅ 性能优势**:
403
- - 避免每次请求都重复转换
404
- - schema 复用,内存占用更小
405
- - 响应时间更稳定
406
-
407
- **✅ 代码优势**:
408
- - 集中管理所有验证规则
409
- - 易于维护和修改
410
- - 类型安全(TypeScript)
411
-
412
- ---
413
-
414
- ## 对应示例文件
415
-
416
- **示例入口**: [best-practices-project-structure.ts](https://github.com/vextjs/schema-dsl/blob/main/examples/docs/best-practices-project-structure.ts)
417
- **说明**: 用一个最小的 `userSchemas` 对象模拟集中定义 / 路由复用结构,直接验证注册与登录两条路径。
1
+ # Schema-DSL 项目最佳实践示例
2
+
3
+ ## 推荐的项目结构
4
+
5
+ ```text
6
+ your-project/
7
+ ├── schemas/ # ✅ 所有 schema 定义(项目启动时加载)
8
+ │ ├── index.js # 统一导出
9
+ │ ├── user.js # 用户相关 schema
10
+ │ ├── order.js # 订单相关 schema
11
+ │ └── product.js # 产品相关 schema
12
+ ├── routes/
13
+ │ ├── user.js # 用户路由(使用 schemas/user.js)
14
+ │ ├── order.js # 订单路由(使用 schemas/order.js)
15
+ │ └── product.js # 产品路由(使用 schemas/product.js)
16
+ └── app.js # 主应用入口
17
+ ```
18
+
19
+ ---
20
+
21
+ ## 完整示例代码
22
+
23
+ ### 1. 定义 Schema(schemas/user.js)
24
+
25
+ ```javascript
26
+ const { dsl } = require('schema-dsl');
27
+
28
+ /**
29
+ * 用户相关的所有 schema
30
+ *
31
+ * ✅ 在项目启动时转换一次,后续直接复用
32
+ * ✅ 避免每次请求都重复转换
33
+ */
34
+ const userSchemas = {
35
+ // 注册 schema
36
+ register: dsl({
37
+ username: dsl('string:3-32!')
38
+ .pattern(/^[a-zA-Z0-9_]+$/)
39
+ .label('用户名')
40
+ .messages({
41
+ 'string.pattern': '用户名只能包含字母、数字和下划线',
42
+ 'string.min': '用户名至少需要3个字符',
43
+ 'string.max': '用户名最多32个字符'
44
+ }),
45
+
46
+ email: dsl('email!')
47
+ .label('邮箱')
48
+ .messages({
49
+ 'string.email': '请输入有效的邮箱地址'
50
+ }),
51
+
52
+ password: dsl('string!').password('strong')
53
+ .label('密码')
54
+ .messages({
55
+ 'string.password': '密码必须包含大小写字母、数字和特殊字符'
56
+ }),
57
+
58
+ age: 'number:18-120',
59
+
60
+ phone: dsl('phone')
61
+ .label('手机号')
62
+ .messages({
63
+ 'string.phone': '请输入有效的手机号'
64
+ })
65
+ }),
66
+
67
+ // 登录 schema
68
+ login: dsl({
69
+ username: 'string!',
70
+ password: 'string!'
71
+ }),
72
+
73
+ // 更新个人资料 schema
74
+ updateProfile: dsl({
75
+ nickname: 'string:2-20',
76
+ avatar: 'url',
77
+ bio: 'string:0-500',
78
+ birthday: 'date',
79
+ gender: 'male|female|other'
80
+ }),
81
+
82
+ // 修改密码 schema
83
+ changePassword: dsl({
84
+ oldPassword: 'string!',
85
+ newPassword: dsl('string!').password('strong')
86
+ })
87
+ };
88
+
89
+ module.exports = userSchemas;
90
+ ```
91
+
92
+ ### 2. 定义 Schema(schemas/order.js)
93
+
94
+ ```javascript
95
+ const { dsl } = require('schema-dsl');
96
+
97
+ const orderSchemas = {
98
+ // 创建订单
99
+ create: dsl({
100
+ items: 'array:1-100<object>!',
101
+ shippingAddress: dsl({
102
+ name: 'string:2-50!',
103
+ phone: 'phone!',
104
+ address: 'string:10-200!',
105
+ zipCode: 'string:6'
106
+ }),
107
+ paymentMethod: 'alipay|wechat|card!',
108
+ couponCode: 'string:6-20'
109
+ }),
110
+
111
+ // 更新订单状态
112
+ updateStatus: dsl({
113
+ status: 'pending|paid|shipped|completed|cancelled!',
114
+ note: 'string:0-500'
115
+ })
116
+ };
117
+
118
+ module.exports = orderSchemas;
119
+ ```
120
+
121
+ ### 3. 统一导出(schemas/index.js)
122
+
123
+ ```javascript
124
+ /**
125
+ * 统一导出所有 schema
126
+ *
127
+ * 使用方式:
128
+ * const schemas = require('./schemas');
129
+ * const result = validate(schemas.user.register, data);
130
+ */
131
+ module.exports = {
132
+ user: require('./user'),
133
+ order: require('./order'),
134
+ product: require('./product')
135
+ };
136
+ ```
137
+
138
+ ### 4. 在路由中使用(routes/user.js)
139
+
140
+ ```javascript
141
+ const express = require('express');
142
+ const router = express.Router();
143
+ const { validate } = require('schema-dsl');
144
+ const userSchemas = require('../schemas/user');
145
+
146
+ /**
147
+ * 用户注册
148
+ *
149
+ * ✅ 使用预定义的 schema,不再重复转换
150
+ */
151
+ router.post('/register', async (req, res) => {
152
+ // ✅ 直接使用,性能最优
153
+ const result = validate(userSchemas.register, req.body);
154
+
155
+ if (!result.valid) {
156
+ return res.status(400).json({
157
+ code: 'VALIDATION_ERROR',
158
+ message: '数据验证失败',
159
+ errors: result.errors
160
+ });
161
+ }
162
+
163
+ // 处理注册逻辑
164
+ try {
165
+ const user = await createUser(result.data);
166
+ res.status(201).json({
167
+ code: 'SUCCESS',
168
+ data: user
169
+ });
170
+ } catch (error) {
171
+ res.status(500).json({
172
+ code: 'SERVER_ERROR',
173
+ message: error.message
174
+ });
175
+ }
176
+ });
177
+
178
+ /**
179
+ * 用户登录
180
+ */
181
+ router.post('/login', async (req, res) => {
182
+ // ✅ 直接使用
183
+ const result = validate(userSchemas.login, req.body);
184
+
185
+ if (!result.valid) {
186
+ return res.status(400).json({
187
+ code: 'VALIDATION_ERROR',
188
+ errors: result.errors
189
+ });
190
+ }
191
+
192
+ // 处理登录逻辑
193
+ // ...
194
+ });
195
+
196
+ /**
197
+ * 更新个人资料
198
+ */
199
+ router.put('/profile', authenticate, async (req, res) => {
200
+ // ✅ 直接使用
201
+ const result = validate(userSchemas.updateProfile, req.body);
202
+
203
+ if (!result.valid) {
204
+ return res.status(400).json({
205
+ code: 'VALIDATION_ERROR',
206
+ errors: result.errors
207
+ });
208
+ }
209
+
210
+ // 处理更新逻辑
211
+ // ...
212
+ });
213
+
214
+ module.exports = router;
215
+ ```
216
+
217
+ ### 5. 主应用入口(app.js)
218
+
219
+ ```javascript
220
+ const express = require('express');
221
+ const app = express();
222
+
223
+ // ✅ 在应用启动时加载所有 schema(只转换一次)
224
+ const schemas = require('./schemas');
225
+ console.log('✅ Schemas loaded:', Object.keys(schemas));
226
+
227
+ // 中间件
228
+ app.use(express.json());
229
+
230
+ // 路由
231
+ app.use('/api/user', require('./routes/user'));
232
+ app.use('/api/order', require('./routes/order'));
233
+ app.use('/api/product', require('./routes/product'));
234
+
235
+ // 启动服务
236
+ const PORT = process.env.PORT || 3000;
237
+ app.listen(PORT, () => {
238
+ console.log(`✅ Server started on port ${PORT}`);
239
+ console.log('✅ All schemas are loaded and ready to validate');
240
+ });
241
+
242
+ module.exports = app;
243
+ ```
244
+
245
+ ---
246
+
247
+ ## 性能对比
248
+
249
+ ### ❌ 不推荐:每次请求都转换
250
+
251
+ ```javascript
252
+ // ❌ 错误示例
253
+ router.post('/register', (req, res) => {
254
+ const result = validate(
255
+ { // ❌ 每次请求都转换
256
+ username: 'string:3-32!',
257
+ email: 'email!',
258
+ password: dsl('string!').password('strong')
259
+ },
260
+ req.body
261
+ );
262
+ // ...
263
+ });
264
+ ```
265
+
266
+ **性能问题**:
267
+ - ❌ 每次请求都执行 DSL → JSON Schema 转换
268
+ - ❌ 1000 次请求 = 1000 次转换
269
+ - ❌ 高并发时性能损失明显
270
+
271
+ ### ✅ 推荐:项目启动时转换
272
+
273
+ ```javascript
274
+ // ✅ 正确示例
275
+ const userSchemas = require('../schemas/user'); // ✅ 启动时加载
276
+
277
+ router.post('/register', (req, res) => {
278
+ const result = validate(
279
+ userSchemas.register, // ✅ 直接使用
280
+ req.body
281
+ );
282
+ // ...
283
+ });
284
+ ```
285
+
286
+ **性能优势**:
287
+ - ✅ 启动时转换 1 次
288
+ - ✅ 1000 次请求 = 0 次转换
289
+ - ✅ 高并发时性能最优
290
+
291
+ ---
292
+
293
+ ## 使用场景总结
294
+
295
+ | 场景 | 推荐方式 | 代码示例 | 原因 |
296
+ |------|---------|---------|------|
297
+ | **生产环境 API** | ✅ 项目启动时配置 | `const schemas = require('./schemas')` | 避免每次请求都转换 |
298
+ | **高并发服务** | ✅ 项目启动时配置 | 同上 | 3-5% 的性能损失会被放大 |
299
+ | **微服务** | ✅ 项目启动时配置 | 同上 | 保证响应时间稳定 |
300
+ | **单次脚本** | ✅ 直接用 DSL 对象(当前版便捷函数支持) | `validate({ email: 'email!' }, data)` | 只执行一次,性能影响可忽略 |
301
+ | **原型开发** | ✅ 直接用 DSL 对象(当前版便捷函数支持) | 同上 | 快速迭代,无需在意性能 |
302
+ | **测试代码** | ✅ 直接用 DSL 对象(当前版便捷函数支持) | 同上 | 简洁清晰,易于维护 |
303
+
304
+ ---
305
+
306
+ ## 常见错误
307
+
308
+ ### ❌ 错误1:在路由文件中定义 schema
309
+
310
+ ```javascript
311
+ // ❌ 不推荐
312
+ router.post('/register', (req, res) => {
313
+ const schema = dsl({ // ❌ 每次请求都创建
314
+ username: 'string:3-32!',
315
+ email: 'email!'
316
+ });
317
+
318
+ const result = validate(schema, req.body);
319
+ // ...
320
+ });
321
+ ```
322
+
323
+ **问题**:每次请求都创建新的 schema 对象,浪费性能。
324
+
325
+ ### ❌ 错误2:在函数内部定义 schema
326
+
327
+ ```javascript
328
+ // ❌ 不推荐
329
+ function validateUser(data) {
330
+ const schema = dsl({ // ❌ 每次调用都创建
331
+ username: 'string:3-32!',
332
+ email: 'email!'
333
+ });
334
+
335
+ return validate(schema, data);
336
+ }
337
+ ```
338
+
339
+ **问题**:每次调用函数都创建新的 schema,应该提到函数外部。
340
+
341
+ ### ✅ 正确:在模块顶部定义
342
+
343
+ ```javascript
344
+ // ✅ 推荐:模块加载时创建一次
345
+ const userSchema = dsl({
346
+ username: 'string:3-32!',
347
+ email: 'email!'
348
+ });
349
+
350
+ router.post('/register', (req, res) => {
351
+ const result = validate(userSchema, req.body); // ✅ 直接使用
352
+ // ...
353
+ });
354
+ ```
355
+
356
+ ---
357
+
358
+ ## TypeScript 支持
359
+
360
+ ```typescript
361
+ // schemas/user.ts
362
+ import { dsl } from 'schema-dsl';
363
+
364
+ export const userSchemas = {
365
+ register: dsl({
366
+ username: dsl('string:3-32!')
367
+ .pattern(/^[a-zA-Z0-9_]+$/)
368
+ .error({ pattern: '只能包含字母、数字和下划线' }),
369
+ email: 'email!',
370
+ password: dsl('string:8-64!')
371
+ .pattern(/^(?=.*[A-Za-z])(?=.*\d).{8,}$/)
372
+ .error({ pattern: '密码至少 8 位且必须包含字母和数字' }),
373
+ age: 'number:18-120'
374
+ }),
375
+
376
+ login: dsl({
377
+ username: 'string!',
378
+ password: 'string!'
379
+ })
380
+ };
381
+
382
+ // routes/user.ts
383
+ import { validate } from 'schema-dsl';
384
+ import { userSchemas } from '../schemas/user';
385
+
386
+ router.post('/register', (req, res) => {
387
+ const result = validate(userSchemas.register, req.body);
388
+ // ...
389
+ });
390
+ ```
391
+
392
+ ---
393
+
394
+ ## 总结
395
+
396
+ **✅ 最佳实践**:
397
+ 1. 在单独的 `schemas/` 目录定义所有 schema
398
+ 2. 项目启动时加载,转换一次
399
+ 3. 路由中直接使用,不再转换
400
+ 4. 适合生产环境和高并发场景
401
+
402
+ **✅ 性能优势**:
403
+ - 避免每次请求都重复转换
404
+ - schema 复用,内存占用更小
405
+ - 响应时间更稳定
406
+
407
+ **✅ 代码优势**:
408
+ - 集中管理所有验证规则
409
+ - 易于维护和修改
410
+ - 类型安全(TypeScript)
411
+
412
+ ---
413
+
414
+ ## 对应示例文件
415
+
416
+ **示例入口**: [best-practices-project-structure.ts](https://github.com/vextjs/schema-dsl/blob/main/examples/docs/best-practices-project-structure.ts)
417
+ **说明**: 用一个最小的 `userSchemas` 对象模拟集中定义 / 路由复用结构,直接验证注册与登录两条路径。