@engjts/nexus 0.1.7 → 0.1.9
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/dist/advanced/playground/generatePlaygroundHTML.d.ts.map +1 -1
- package/dist/advanced/playground/generatePlaygroundHTML.js +107 -0
- package/dist/advanced/playground/generatePlaygroundHTML.js.map +1 -1
- package/dist/advanced/playground/playground.d.ts +19 -0
- package/dist/advanced/playground/playground.d.ts.map +1 -1
- package/dist/advanced/playground/playground.js +70 -0
- package/dist/advanced/playground/playground.js.map +1 -1
- package/dist/advanced/playground/types.d.ts +20 -0
- package/dist/advanced/playground/types.d.ts.map +1 -1
- package/dist/core/application.d.ts +14 -0
- package/dist/core/application.d.ts.map +1 -1
- package/dist/core/application.js +173 -71
- package/dist/core/application.js.map +1 -1
- package/dist/core/context-pool.d.ts +2 -13
- package/dist/core/context-pool.d.ts.map +1 -1
- package/dist/core/context-pool.js +7 -45
- package/dist/core/context-pool.js.map +1 -1
- package/dist/core/context.d.ts +108 -5
- package/dist/core/context.d.ts.map +1 -1
- package/dist/core/context.js +449 -53
- package/dist/core/context.js.map +1 -1
- package/dist/core/index.d.ts +1 -0
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +9 -1
- package/dist/core/index.js.map +1 -1
- package/dist/core/middleware.d.ts +6 -0
- package/dist/core/middleware.d.ts.map +1 -1
- package/dist/core/middleware.js +83 -84
- package/dist/core/middleware.js.map +1 -1
- package/dist/core/performance/fast-json.d.ts +149 -0
- package/dist/core/performance/fast-json.d.ts.map +1 -0
- package/dist/core/performance/fast-json.js +473 -0
- package/dist/core/performance/fast-json.js.map +1 -0
- package/dist/core/router/file-router.d.ts +20 -7
- package/dist/core/router/file-router.d.ts.map +1 -1
- package/dist/core/router/file-router.js +41 -13
- package/dist/core/router/file-router.js.map +1 -1
- package/dist/core/router/index.d.ts +6 -0
- package/dist/core/router/index.d.ts.map +1 -1
- package/dist/core/router/index.js +33 -6
- package/dist/core/router/index.js.map +1 -1
- package/dist/core/router/radix-tree.d.ts +4 -1
- package/dist/core/router/radix-tree.d.ts.map +1 -1
- package/dist/core/router/radix-tree.js +7 -3
- package/dist/core/router/radix-tree.js.map +1 -1
- package/dist/core/serializer.d.ts +251 -0
- package/dist/core/serializer.d.ts.map +1 -0
- package/dist/core/serializer.js +290 -0
- package/dist/core/serializer.js.map +1 -0
- package/dist/core/types.d.ts +39 -1
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +12 -2
- package/dist/index.js.map +1 -1
- package/package.json +3 -1
- package/documentation/01-getting-started.md +0 -240
- package/documentation/02-context.md +0 -335
- package/documentation/03-routing.md +0 -397
- package/documentation/04-middleware.md +0 -483
- package/documentation/05-validation.md +0 -514
- package/documentation/06-error-handling.md +0 -465
- package/documentation/07-performance.md +0 -364
- package/documentation/08-adapters.md +0 -470
- package/documentation/09-api-reference.md +0 -548
- package/documentation/10-examples.md +0 -582
- package/documentation/11-deployment.md +0 -477
- package/documentation/12-sentry.md +0 -620
- package/documentation/13-sentry-data-storage.md +0 -996
- package/documentation/14-sentry-data-reference.md +0 -457
- package/documentation/15-sentry-summary.md +0 -409
- package/documentation/16-alerts-system.md +0 -745
- package/documentation/17-alert-adapters.md +0 -696
- package/documentation/18-alerts-implementation-summary.md +0 -385
- package/documentation/19-class-based-routing.md +0 -840
- package/documentation/20-websocket-realtime.md +0 -813
- package/documentation/21-cache-system.md +0 -510
- package/documentation/22-job-queue.md +0 -772
- package/documentation/23-sentry-plugin.md +0 -551
- package/documentation/24-testing-utilities.md +0 -1287
- package/documentation/25-api-versioning.md +0 -533
- package/documentation/26-context-store.md +0 -607
- package/documentation/27-dependency-injection.md +0 -329
- package/documentation/28-lifecycle-hooks.md +0 -521
- package/documentation/29-package-structure.md +0 -196
- package/documentation/30-plugin-system.md +0 -414
- package/documentation/31-jwt-authentication.md +0 -597
- package/documentation/32-cli.md +0 -268
- package/documentation/ALERTS-COMPLETE-SUMMARY.md +0 -429
- package/documentation/ALERTS-INDEX.md +0 -330
- package/documentation/ALERTS-QUICK-REFERENCE.md +0 -286
- package/documentation/README.md +0 -178
- package/documentation/index.html +0 -34
- package/modern_framework_paper.md +0 -1870
- package/public/css/style.css +0 -87
- package/public/index.html +0 -34
- package/public/js/app.js +0 -27
- package/src/advanced/cache/InMemoryCacheStore.ts +0 -68
- package/src/advanced/cache/MultiTierCache.ts +0 -194
- package/src/advanced/cache/RedisCacheStore.ts +0 -341
- package/src/advanced/cache/index.ts +0 -5
- package/src/advanced/cache/types.ts +0 -40
- package/src/advanced/graphql/SimpleDataLoader.ts +0 -42
- package/src/advanced/graphql/index.ts +0 -22
- package/src/advanced/graphql/server.ts +0 -252
- package/src/advanced/graphql/types.ts +0 -42
- package/src/advanced/jobs/InMemoryQueueStore.ts +0 -68
- package/src/advanced/jobs/JobQueue.ts +0 -556
- package/src/advanced/jobs/RedisQueueStore.ts +0 -367
- package/src/advanced/jobs/index.ts +0 -5
- package/src/advanced/jobs/types.ts +0 -70
- package/src/advanced/observability/APMManager.ts +0 -163
- package/src/advanced/observability/AlertManager.ts +0 -109
- package/src/advanced/observability/MetricRegistry.ts +0 -151
- package/src/advanced/observability/ObservabilityCenter.ts +0 -304
- package/src/advanced/observability/StructuredLogger.ts +0 -154
- package/src/advanced/observability/TracingManager.ts +0 -117
- package/src/advanced/observability/adapters.ts +0 -304
- package/src/advanced/observability/createObservabilityMiddleware.ts +0 -63
- package/src/advanced/observability/index.ts +0 -11
- package/src/advanced/observability/types.ts +0 -174
- package/src/advanced/playground/extractPathParams.ts +0 -6
- package/src/advanced/playground/generateFieldExample.ts +0 -31
- package/src/advanced/playground/generatePlaygroundHTML.ts +0 -1849
- package/src/advanced/playground/generateSummary.ts +0 -19
- package/src/advanced/playground/getTagFromPath.ts +0 -9
- package/src/advanced/playground/index.ts +0 -8
- package/src/advanced/playground/playground.ts +0 -170
- package/src/advanced/playground/types.ts +0 -20
- package/src/advanced/playground/zodToExample.ts +0 -16
- package/src/advanced/playground/zodToParams.ts +0 -15
- package/src/advanced/postman/buildAuth.ts +0 -31
- package/src/advanced/postman/buildBody.ts +0 -15
- package/src/advanced/postman/buildQueryParams.ts +0 -27
- package/src/advanced/postman/buildRequestItem.ts +0 -36
- package/src/advanced/postman/buildResponses.ts +0 -11
- package/src/advanced/postman/buildUrl.ts +0 -33
- package/src/advanced/postman/capitalize.ts +0 -4
- package/src/advanced/postman/generateCollection.ts +0 -59
- package/src/advanced/postman/generateEnvironment.ts +0 -34
- package/src/advanced/postman/generateExampleFromZod.ts +0 -21
- package/src/advanced/postman/generateFieldExample.ts +0 -45
- package/src/advanced/postman/generateName.ts +0 -20
- package/src/advanced/postman/generateUUID.ts +0 -11
- package/src/advanced/postman/getTagFromPath.ts +0 -10
- package/src/advanced/postman/index.ts +0 -28
- package/src/advanced/postman/postman.ts +0 -156
- package/src/advanced/postman/slugify.ts +0 -7
- package/src/advanced/postman/types.ts +0 -140
- package/src/advanced/realtime/index.ts +0 -18
- package/src/advanced/realtime/websocket.ts +0 -231
- package/src/advanced/sentry/index.ts +0 -1236
- package/src/advanced/sentry/types.ts +0 -355
- package/src/advanced/static/generateDirectoryListing.ts +0 -47
- package/src/advanced/static/generateETag.ts +0 -7
- package/src/advanced/static/getMimeType.ts +0 -9
- package/src/advanced/static/index.ts +0 -32
- package/src/advanced/static/isSafePath.ts +0 -13
- package/src/advanced/static/publicDir.ts +0 -21
- package/src/advanced/static/serveStatic.ts +0 -225
- package/src/advanced/static/spa.ts +0 -24
- package/src/advanced/static/types.ts +0 -159
- package/src/advanced/swagger/SwaggerGenerator.ts +0 -66
- package/src/advanced/swagger/buildOperation.ts +0 -61
- package/src/advanced/swagger/buildParameters.ts +0 -61
- package/src/advanced/swagger/buildRequestBody.ts +0 -21
- package/src/advanced/swagger/buildResponses.ts +0 -54
- package/src/advanced/swagger/capitalize.ts +0 -5
- package/src/advanced/swagger/convertPath.ts +0 -9
- package/src/advanced/swagger/createSwagger.ts +0 -12
- package/src/advanced/swagger/generateOperationId.ts +0 -21
- package/src/advanced/swagger/generateSpec.ts +0 -105
- package/src/advanced/swagger/generateSummary.ts +0 -24
- package/src/advanced/swagger/generateSwaggerUI.ts +0 -70
- package/src/advanced/swagger/generateThemeCss.ts +0 -53
- package/src/advanced/swagger/index.ts +0 -25
- package/src/advanced/swagger/swagger.ts +0 -237
- package/src/advanced/swagger/types.ts +0 -206
- package/src/advanced/swagger/zodFieldToOpenAPI.ts +0 -94
- package/src/advanced/swagger/zodSchemaToOpenAPI.ts +0 -50
- package/src/advanced/swagger/zodToOpenAPI.ts +0 -22
- package/src/advanced/testing/factory.ts +0 -509
- package/src/advanced/testing/harness.ts +0 -612
- package/src/advanced/testing/index.ts +0 -430
- package/src/advanced/testing/load-test.ts +0 -618
- package/src/advanced/testing/mock-server.ts +0 -498
- package/src/advanced/testing/mock.ts +0 -670
- package/src/cli/bin.ts +0 -9
- package/src/cli/cli.ts +0 -158
- package/src/cli/commands/add.ts +0 -178
- package/src/cli/commands/build.ts +0 -73
- package/src/cli/commands/create.ts +0 -166
- package/src/cli/commands/dev.ts +0 -85
- package/src/cli/commands/generate.ts +0 -99
- package/src/cli/commands/help.ts +0 -95
- package/src/cli/commands/init.ts +0 -91
- package/src/cli/commands/version.ts +0 -38
- package/src/cli/index.ts +0 -6
- package/src/cli/templates/generators.ts +0 -359
- package/src/cli/templates/index.ts +0 -680
- package/src/cli/utils/exec.ts +0 -52
- package/src/cli/utils/file-system.ts +0 -78
- package/src/cli/utils/logger.ts +0 -111
- package/src/core/adapter.ts +0 -88
- package/src/core/application.ts +0 -1335
- package/src/core/context-pool.ts +0 -127
- package/src/core/context.ts +0 -412
- package/src/core/index.ts +0 -80
- package/src/core/middleware.ts +0 -262
- package/src/core/performance/buffer-pool.ts +0 -108
- package/src/core/performance/middleware-optimizer.ts +0 -162
- package/src/core/plugin/PluginManager.ts +0 -435
- package/src/core/plugin/builder.ts +0 -358
- package/src/core/plugin/index.ts +0 -50
- package/src/core/plugin/types.ts +0 -214
- package/src/core/router/file-router.ts +0 -594
- package/src/core/router/index.ts +0 -227
- package/src/core/router/radix-tree.ts +0 -226
- package/src/core/store/index.ts +0 -30
- package/src/core/store/registry.ts +0 -178
- package/src/core/store/request-store.ts +0 -240
- package/src/core/store/types.ts +0 -233
- package/src/core/types.ts +0 -574
- package/src/database/adapter.ts +0 -35
- package/src/database/adapters/index.ts +0 -1
- package/src/database/adapters/mysql.ts +0 -669
- package/src/database/database.ts +0 -70
- package/src/database/dialect.ts +0 -388
- package/src/database/index.ts +0 -12
- package/src/database/migrations.ts +0 -86
- package/src/database/optimizer.ts +0 -125
- package/src/database/query-builder.ts +0 -404
- package/src/database/realtime.ts +0 -53
- package/src/database/schema.ts +0 -71
- package/src/database/transactions.ts +0 -56
- package/src/database/types.ts +0 -87
- package/src/deployment/cluster.ts +0 -471
- package/src/deployment/config.ts +0 -454
- package/src/deployment/docker.ts +0 -599
- package/src/deployment/graceful-shutdown.ts +0 -373
- package/src/deployment/index.ts +0 -56
- package/src/index.ts +0 -264
- package/src/security/adapter.ts +0 -318
- package/src/security/auth/JWTPlugin.ts +0 -234
- package/src/security/auth/JWTProvider.ts +0 -316
- package/src/security/auth/adapter.ts +0 -12
- package/src/security/auth/jwt.ts +0 -234
- package/src/security/auth/middleware.ts +0 -188
- package/src/security/csrf.ts +0 -220
- package/src/security/headers.ts +0 -108
- package/src/security/index.ts +0 -60
- package/src/security/rate-limit/adapter.ts +0 -7
- package/src/security/rate-limit/memory.ts +0 -108
- package/src/security/rate-limit/middleware.ts +0 -181
- package/src/security/sanitization.ts +0 -75
- package/src/security/types.ts +0 -240
- package/src/security/utils.ts +0 -52
- package/tsconfig.json +0 -39
|
@@ -1,514 +0,0 @@
|
|
|
1
|
-
# Schema Validation
|
|
2
|
-
|
|
3
|
-
## Overview
|
|
4
|
-
|
|
5
|
-
Nexus integrates **Zod** for type-safe schema validation. Validation happens automatically before your handler runs, with clear error responses for invalid data.
|
|
6
|
-
|
|
7
|
-
## Installation
|
|
8
|
-
|
|
9
|
-
Zod is included as a dependency:
|
|
10
|
-
|
|
11
|
-
```bash
|
|
12
|
-
npm install zod
|
|
13
|
-
```
|
|
14
|
-
|
|
15
|
-
## Basic Validation
|
|
16
|
-
|
|
17
|
-
### Body Validation
|
|
18
|
-
|
|
19
|
-
```typescript
|
|
20
|
-
import { z } from 'zod';
|
|
21
|
-
|
|
22
|
-
app.post('/users', {
|
|
23
|
-
schema: {
|
|
24
|
-
body: z.object({
|
|
25
|
-
name: z.string().min(2).max(50),
|
|
26
|
-
email: z.string().email(),
|
|
27
|
-
age: z.number().min(18).max(120)
|
|
28
|
-
})
|
|
29
|
-
},
|
|
30
|
-
handler: async (ctx) => {
|
|
31
|
-
// ctx.body is now typed and validated
|
|
32
|
-
const { name, email, age } = ctx.body;
|
|
33
|
-
return { user: { name, email, age } };
|
|
34
|
-
}
|
|
35
|
-
});
|
|
36
|
-
```
|
|
37
|
-
|
|
38
|
-
### Query Validation
|
|
39
|
-
|
|
40
|
-
```typescript
|
|
41
|
-
app.get('/search', {
|
|
42
|
-
schema: {
|
|
43
|
-
query: z.object({
|
|
44
|
-
q: z.string().min(1),
|
|
45
|
-
page: z.string().regex(/^\d+$/).transform(Number).default('1'),
|
|
46
|
-
limit: z.string().regex(/^\d+$/).transform(Number).default('10')
|
|
47
|
-
})
|
|
48
|
-
},
|
|
49
|
-
handler: async (ctx) => {
|
|
50
|
-
const { q, page, limit } = ctx.query;
|
|
51
|
-
return await search(q, page, limit);
|
|
52
|
-
}
|
|
53
|
-
});
|
|
54
|
-
```
|
|
55
|
-
|
|
56
|
-
### Params Validation
|
|
57
|
-
|
|
58
|
-
```typescript
|
|
59
|
-
app.get('/users/:id', {
|
|
60
|
-
schema: {
|
|
61
|
-
params: z.object({
|
|
62
|
-
id: z.string().uuid()
|
|
63
|
-
})
|
|
64
|
-
},
|
|
65
|
-
handler: async (ctx) => {
|
|
66
|
-
const { id } = ctx.params; // Validated UUID
|
|
67
|
-
return await getUser(id);
|
|
68
|
-
}
|
|
69
|
-
});
|
|
70
|
-
```
|
|
71
|
-
|
|
72
|
-
### Headers Validation
|
|
73
|
-
|
|
74
|
-
```typescript
|
|
75
|
-
app.post('/api/data', {
|
|
76
|
-
schema: {
|
|
77
|
-
headers: z.object({
|
|
78
|
-
'content-type': z.literal('application/json'),
|
|
79
|
-
'authorization': z.string().startsWith('Bearer ')
|
|
80
|
-
})
|
|
81
|
-
},
|
|
82
|
-
handler: async (ctx) => {
|
|
83
|
-
// Headers are validated
|
|
84
|
-
return { success: true };
|
|
85
|
-
}
|
|
86
|
-
});
|
|
87
|
-
```
|
|
88
|
-
|
|
89
|
-
## Validation Types
|
|
90
|
-
|
|
91
|
-
### String Validation
|
|
92
|
-
|
|
93
|
-
```typescript
|
|
94
|
-
const schema = z.object({
|
|
95
|
-
// Basic string
|
|
96
|
-
name: z.string(),
|
|
97
|
-
|
|
98
|
-
// Length constraints
|
|
99
|
-
username: z.string().min(3).max(20),
|
|
100
|
-
|
|
101
|
-
// Email
|
|
102
|
-
email: z.string().email(),
|
|
103
|
-
|
|
104
|
-
// URL
|
|
105
|
-
website: z.string().url(),
|
|
106
|
-
|
|
107
|
-
// UUID
|
|
108
|
-
id: z.string().uuid(),
|
|
109
|
-
|
|
110
|
-
// Regex pattern
|
|
111
|
-
phoneNumber: z.string().regex(/^\+?[1-9]\d{1,14}$/),
|
|
112
|
-
|
|
113
|
-
// Enum
|
|
114
|
-
role: z.enum(['admin', 'user', 'guest']),
|
|
115
|
-
|
|
116
|
-
// Transform
|
|
117
|
-
slug: z.string().toLowerCase().trim()
|
|
118
|
-
});
|
|
119
|
-
```
|
|
120
|
-
|
|
121
|
-
### Number Validation
|
|
122
|
-
|
|
123
|
-
```typescript
|
|
124
|
-
const schema = z.object({
|
|
125
|
-
// Basic number
|
|
126
|
-
age: z.number(),
|
|
127
|
-
|
|
128
|
-
// Range
|
|
129
|
-
rating: z.number().min(1).max(5),
|
|
130
|
-
|
|
131
|
-
// Integer
|
|
132
|
-
count: z.number().int(),
|
|
133
|
-
|
|
134
|
-
// Positive/Negative
|
|
135
|
-
price: z.number().positive(),
|
|
136
|
-
balance: z.number().negative().optional(),
|
|
137
|
-
|
|
138
|
-
// Transform from string
|
|
139
|
-
page: z.string().transform(Number)
|
|
140
|
-
});
|
|
141
|
-
```
|
|
142
|
-
|
|
143
|
-
### Boolean Validation
|
|
144
|
-
|
|
145
|
-
```typescript
|
|
146
|
-
const schema = z.object({
|
|
147
|
-
// Basic boolean
|
|
148
|
-
isActive: z.boolean(),
|
|
149
|
-
|
|
150
|
-
// Transform from string
|
|
151
|
-
enabled: z.string()
|
|
152
|
-
.transform(val => val === 'true')
|
|
153
|
-
.pipe(z.boolean())
|
|
154
|
-
});
|
|
155
|
-
```
|
|
156
|
-
|
|
157
|
-
### Array Validation
|
|
158
|
-
|
|
159
|
-
```typescript
|
|
160
|
-
const schema = z.object({
|
|
161
|
-
// Array of strings
|
|
162
|
-
tags: z.array(z.string()),
|
|
163
|
-
|
|
164
|
-
// Min/Max length
|
|
165
|
-
items: z.array(z.string()).min(1).max(10),
|
|
166
|
-
|
|
167
|
-
// Array of objects
|
|
168
|
-
users: z.array(z.object({
|
|
169
|
-
id: z.string(),
|
|
170
|
-
name: z.string()
|
|
171
|
-
})),
|
|
172
|
-
|
|
173
|
-
// Non-empty array
|
|
174
|
-
categories: z.array(z.string()).nonempty()
|
|
175
|
-
});
|
|
176
|
-
```
|
|
177
|
-
|
|
178
|
-
### Object Validation
|
|
179
|
-
|
|
180
|
-
```typescript
|
|
181
|
-
const schema = z.object({
|
|
182
|
-
// Nested object
|
|
183
|
-
address: z.object({
|
|
184
|
-
street: z.string(),
|
|
185
|
-
city: z.string(),
|
|
186
|
-
zipCode: z.string().regex(/^\d{5}$/)
|
|
187
|
-
}),
|
|
188
|
-
|
|
189
|
-
// Optional nested object
|
|
190
|
-
billing: z.object({
|
|
191
|
-
cardNumber: z.string(),
|
|
192
|
-
cvv: z.string()
|
|
193
|
-
}).optional()
|
|
194
|
-
});
|
|
195
|
-
```
|
|
196
|
-
|
|
197
|
-
## Optional & Default Values
|
|
198
|
-
|
|
199
|
-
### Optional Fields
|
|
200
|
-
|
|
201
|
-
```typescript
|
|
202
|
-
const schema = z.object({
|
|
203
|
-
name: z.string(),
|
|
204
|
-
email: z.string().email(),
|
|
205
|
-
age: z.number().optional(), // Can be undefined
|
|
206
|
-
phone: z.string().optional()
|
|
207
|
-
});
|
|
208
|
-
```
|
|
209
|
-
|
|
210
|
-
### Default Values
|
|
211
|
-
|
|
212
|
-
```typescript
|
|
213
|
-
const schema = z.object({
|
|
214
|
-
page: z.number().default(1),
|
|
215
|
-
limit: z.number().default(10),
|
|
216
|
-
sortBy: z.string().default('createdAt')
|
|
217
|
-
});
|
|
218
|
-
```
|
|
219
|
-
|
|
220
|
-
### Nullable Fields
|
|
221
|
-
|
|
222
|
-
```typescript
|
|
223
|
-
const schema = z.object({
|
|
224
|
-
middleName: z.string().nullable(), // Can be null
|
|
225
|
-
notes: z.string().nullish() // Can be null or undefined
|
|
226
|
-
});
|
|
227
|
-
```
|
|
228
|
-
|
|
229
|
-
## Complex Validation
|
|
230
|
-
|
|
231
|
-
### Conditional Validation
|
|
232
|
-
|
|
233
|
-
```typescript
|
|
234
|
-
const schema = z.object({
|
|
235
|
-
type: z.enum(['individual', 'company']),
|
|
236
|
-
name: z.string(),
|
|
237
|
-
|
|
238
|
-
// Conditional fields
|
|
239
|
-
companyName: z.string().optional(),
|
|
240
|
-
taxId: z.string().optional()
|
|
241
|
-
}).refine(
|
|
242
|
-
data => {
|
|
243
|
-
if (data.type === 'company') {
|
|
244
|
-
return !!data.companyName && !!data.taxId;
|
|
245
|
-
}
|
|
246
|
-
return true;
|
|
247
|
-
},
|
|
248
|
-
{
|
|
249
|
-
message: 'Company type requires companyName and taxId',
|
|
250
|
-
path: ['type']
|
|
251
|
-
}
|
|
252
|
-
);
|
|
253
|
-
```
|
|
254
|
-
|
|
255
|
-
### Custom Validation
|
|
256
|
-
|
|
257
|
-
```typescript
|
|
258
|
-
const schema = z.object({
|
|
259
|
-
password: z.string()
|
|
260
|
-
.min(8, 'Password must be at least 8 characters')
|
|
261
|
-
.refine(
|
|
262
|
-
val => /[A-Z]/.test(val),
|
|
263
|
-
'Password must contain uppercase letter'
|
|
264
|
-
)
|
|
265
|
-
.refine(
|
|
266
|
-
val => /[0-9]/.test(val),
|
|
267
|
-
'Password must contain a number'
|
|
268
|
-
),
|
|
269
|
-
|
|
270
|
-
confirmPassword: z.string()
|
|
271
|
-
}).refine(
|
|
272
|
-
data => data.password === data.confirmPassword,
|
|
273
|
-
{
|
|
274
|
-
message: 'Passwords do not match',
|
|
275
|
-
path: ['confirmPassword']
|
|
276
|
-
}
|
|
277
|
-
);
|
|
278
|
-
```
|
|
279
|
-
|
|
280
|
-
### Async Validation
|
|
281
|
-
|
|
282
|
-
```typescript
|
|
283
|
-
const schema = z.object({
|
|
284
|
-
email: z.string().email()
|
|
285
|
-
}).refine(
|
|
286
|
-
async (data) => {
|
|
287
|
-
const exists = await checkEmailExists(data.email);
|
|
288
|
-
return !exists;
|
|
289
|
-
},
|
|
290
|
-
{ message: 'Email already exists' }
|
|
291
|
-
);
|
|
292
|
-
```
|
|
293
|
-
|
|
294
|
-
## Error Handling
|
|
295
|
-
|
|
296
|
-
### Automatic Error Response
|
|
297
|
-
|
|
298
|
-
When validation fails, Nexus automatically returns:
|
|
299
|
-
|
|
300
|
-
```json
|
|
301
|
-
{
|
|
302
|
-
"error": "Validation failed",
|
|
303
|
-
"details": [
|
|
304
|
-
{
|
|
305
|
-
"code": "too_small",
|
|
306
|
-
"minimum": 2,
|
|
307
|
-
"type": "string",
|
|
308
|
-
"inclusive": true,
|
|
309
|
-
"message": "String must contain at least 2 character(s)",
|
|
310
|
-
"path": ["name"]
|
|
311
|
-
}
|
|
312
|
-
]
|
|
313
|
-
}
|
|
314
|
-
```
|
|
315
|
-
|
|
316
|
-
### Custom Error Messages
|
|
317
|
-
|
|
318
|
-
```typescript
|
|
319
|
-
const schema = z.object({
|
|
320
|
-
name: z.string().min(2, 'Name must be at least 2 characters'),
|
|
321
|
-
email: z.string().email('Please provide a valid email'),
|
|
322
|
-
age: z.number().min(18, 'You must be at least 18 years old')
|
|
323
|
-
});
|
|
324
|
-
```
|
|
325
|
-
|
|
326
|
-
## Real-World Examples
|
|
327
|
-
|
|
328
|
-
### User Registration
|
|
329
|
-
|
|
330
|
-
```typescript
|
|
331
|
-
const registerSchema = z.object({
|
|
332
|
-
username: z.string()
|
|
333
|
-
.min(3, 'Username must be at least 3 characters')
|
|
334
|
-
.max(20, 'Username must be at most 20 characters')
|
|
335
|
-
.regex(/^[a-zA-Z0-9_]+$/, 'Username can only contain letters, numbers, and underscores'),
|
|
336
|
-
|
|
337
|
-
email: z.string()
|
|
338
|
-
.email('Invalid email address'),
|
|
339
|
-
|
|
340
|
-
password: z.string()
|
|
341
|
-
.min(8, 'Password must be at least 8 characters')
|
|
342
|
-
.regex(/[A-Z]/, 'Password must contain at least one uppercase letter')
|
|
343
|
-
.regex(/[a-z]/, 'Password must contain at least one lowercase letter')
|
|
344
|
-
.regex(/[0-9]/, 'Password must contain at least one number'),
|
|
345
|
-
|
|
346
|
-
confirmPassword: z.string(),
|
|
347
|
-
|
|
348
|
-
acceptTerms: z.boolean()
|
|
349
|
-
.refine(val => val === true, 'You must accept the terms and conditions')
|
|
350
|
-
}).refine(
|
|
351
|
-
data => data.password === data.confirmPassword,
|
|
352
|
-
{
|
|
353
|
-
message: 'Passwords do not match',
|
|
354
|
-
path: ['confirmPassword']
|
|
355
|
-
}
|
|
356
|
-
);
|
|
357
|
-
|
|
358
|
-
app.post('/auth/register', {
|
|
359
|
-
schema: { body: registerSchema },
|
|
360
|
-
handler: async (ctx) => {
|
|
361
|
-
const user = await createUser(ctx.body);
|
|
362
|
-
return { user };
|
|
363
|
-
}
|
|
364
|
-
});
|
|
365
|
-
```
|
|
366
|
-
|
|
367
|
-
### Pagination & Filtering
|
|
368
|
-
|
|
369
|
-
```typescript
|
|
370
|
-
const listUsersSchema = {
|
|
371
|
-
query: z.object({
|
|
372
|
-
page: z.string()
|
|
373
|
-
.regex(/^\d+$/, 'Page must be a number')
|
|
374
|
-
.transform(Number)
|
|
375
|
-
.pipe(z.number().min(1))
|
|
376
|
-
.default('1'),
|
|
377
|
-
|
|
378
|
-
limit: z.string()
|
|
379
|
-
.regex(/^\d+$/, 'Limit must be a number')
|
|
380
|
-
.transform(Number)
|
|
381
|
-
.pipe(z.number().min(1).max(100))
|
|
382
|
-
.default('10'),
|
|
383
|
-
|
|
384
|
-
sortBy: z.enum(['name', 'email', 'createdAt'])
|
|
385
|
-
.default('createdAt'),
|
|
386
|
-
|
|
387
|
-
order: z.enum(['asc', 'desc'])
|
|
388
|
-
.default('desc'),
|
|
389
|
-
|
|
390
|
-
role: z.enum(['admin', 'user', 'guest'])
|
|
391
|
-
.optional(),
|
|
392
|
-
|
|
393
|
-
search: z.string()
|
|
394
|
-
.min(3, 'Search query must be at least 3 characters')
|
|
395
|
-
.optional()
|
|
396
|
-
})
|
|
397
|
-
};
|
|
398
|
-
|
|
399
|
-
app.get('/users', {
|
|
400
|
-
schema: listUsersSchema,
|
|
401
|
-
handler: async (ctx) => {
|
|
402
|
-
const { page, limit, sortBy, order, role, search } = ctx.query;
|
|
403
|
-
const users = await getUsers({ page, limit, sortBy, order, role, search });
|
|
404
|
-
return { users, page, limit };
|
|
405
|
-
}
|
|
406
|
-
});
|
|
407
|
-
```
|
|
408
|
-
|
|
409
|
-
### File Upload Metadata
|
|
410
|
-
|
|
411
|
-
```typescript
|
|
412
|
-
const uploadSchema = z.object({
|
|
413
|
-
filename: z.string()
|
|
414
|
-
.regex(/^[a-zA-Z0-9_-]+\.[a-z]{2,4}$/, 'Invalid filename'),
|
|
415
|
-
|
|
416
|
-
size: z.number()
|
|
417
|
-
.max(10 * 1024 * 1024, 'File size must be less than 10MB'),
|
|
418
|
-
|
|
419
|
-
mimeType: z.enum([
|
|
420
|
-
'image/jpeg',
|
|
421
|
-
'image/png',
|
|
422
|
-
'image/webp',
|
|
423
|
-
'application/pdf'
|
|
424
|
-
], { errorMap: () => ({ message: 'Unsupported file type' }) }),
|
|
425
|
-
|
|
426
|
-
description: z.string()
|
|
427
|
-
.max(500, 'Description must be at most 500 characters')
|
|
428
|
-
.optional()
|
|
429
|
-
});
|
|
430
|
-
```
|
|
431
|
-
|
|
432
|
-
## Type Inference
|
|
433
|
-
|
|
434
|
-
Zod schemas provide TypeScript type inference:
|
|
435
|
-
|
|
436
|
-
```typescript
|
|
437
|
-
const userSchema = z.object({
|
|
438
|
-
name: z.string(),
|
|
439
|
-
email: z.string().email(),
|
|
440
|
-
age: z.number().optional()
|
|
441
|
-
});
|
|
442
|
-
|
|
443
|
-
// Infer the type
|
|
444
|
-
type User = z.infer<typeof userSchema>;
|
|
445
|
-
// { name: string; email: string; age?: number }
|
|
446
|
-
|
|
447
|
-
const createUser = (user: User) => {
|
|
448
|
-
// Type-safe function
|
|
449
|
-
};
|
|
450
|
-
```
|
|
451
|
-
|
|
452
|
-
## Best Practices
|
|
453
|
-
|
|
454
|
-
### ✅ DO: Use descriptive error messages
|
|
455
|
-
|
|
456
|
-
```typescript
|
|
457
|
-
const schema = z.object({
|
|
458
|
-
age: z.number()
|
|
459
|
-
.min(18, 'You must be at least 18 years old to register')
|
|
460
|
-
});
|
|
461
|
-
```
|
|
462
|
-
|
|
463
|
-
### ✅ DO: Transform data when needed
|
|
464
|
-
|
|
465
|
-
```typescript
|
|
466
|
-
const schema = z.object({
|
|
467
|
-
email: z.string().email().toLowerCase().trim(),
|
|
468
|
-
tags: z.string().transform(val => val.split(',').map(t => t.trim()))
|
|
469
|
-
});
|
|
470
|
-
```
|
|
471
|
-
|
|
472
|
-
### ✅ DO: Reuse schemas
|
|
473
|
-
|
|
474
|
-
```typescript
|
|
475
|
-
const addressSchema = z.object({
|
|
476
|
-
street: z.string(),
|
|
477
|
-
city: z.string(),
|
|
478
|
-
zipCode: z.string()
|
|
479
|
-
});
|
|
480
|
-
|
|
481
|
-
const userSchema = z.object({
|
|
482
|
-
name: z.string(),
|
|
483
|
-
address: addressSchema,
|
|
484
|
-
billingAddress: addressSchema.optional()
|
|
485
|
-
});
|
|
486
|
-
```
|
|
487
|
-
|
|
488
|
-
### ❌ DON'T: Over-validate
|
|
489
|
-
|
|
490
|
-
```typescript
|
|
491
|
-
// Too restrictive
|
|
492
|
-
const schema = z.object({
|
|
493
|
-
name: z.string()
|
|
494
|
-
.min(2).max(50)
|
|
495
|
-
.regex(/^[A-Za-z\s'-]+$/)
|
|
496
|
-
.transform(val => val.trim())
|
|
497
|
-
.refine(val => !val.includes(' '))
|
|
498
|
-
});
|
|
499
|
-
|
|
500
|
-
// Better
|
|
501
|
-
const schema = z.object({
|
|
502
|
-
name: z.string().min(2).max(50).trim()
|
|
503
|
-
});
|
|
504
|
-
```
|
|
505
|
-
|
|
506
|
-
## Next Steps
|
|
507
|
-
|
|
508
|
-
- 🔒 [Error Handling](./06-error-handling.md) - Handle validation errors
|
|
509
|
-
- 🔌 [Middleware](./04-middleware.md) - Validate in middleware
|
|
510
|
-
- 📖 [API Reference](./09-api-reference.md) - Complete API docs
|
|
511
|
-
|
|
512
|
-
---
|
|
513
|
-
|
|
514
|
-
[← Middleware](./04-middleware.md) | [Error Handling →](./06-error-handling.md)
|