0
# Schema Validation
1
2
The schema validation system in @nuxt/schema provides default configuration schema and validation system that ensures proper Nuxt configuration through the `untyped` library integration.
3
4
## Core Schema Types
5
6
### Schema Definition
7
8
```typescript { .api }
9
type SchemaDefinition = import('untyped').SchemaDefinition
10
11
const NuxtConfigSchema: SchemaDefinition
12
```
13
14
The default export from @nuxt/schema combines all configuration resolvers:
15
16
```typescript { .api }
17
const NuxtConfigSchema = {
18
...adhoc,
19
...app,
20
...build,
21
...common,
22
...dev,
23
...experimental,
24
...generate,
25
...internal,
26
...nitro,
27
...postcss,
28
...router,
29
...typescript,
30
...esbuild,
31
...oxc,
32
...vite,
33
...webpack,
34
}
35
```
36
37
## Configuration Resolvers
38
39
### Common Configuration Resolvers
40
41
The common configuration module defines core Nuxt options:
42
43
```typescript { .api }
44
const commonResolvers = {
45
extends: undefined,
46
compatibilityDate: undefined,
47
theme: undefined,
48
49
rootDir: {
50
$resolve: (val: unknown) => typeof val === 'string' ? resolve(val) : process.cwd()
51
},
52
53
workspaceDir: {
54
$resolve: async (val: unknown, get: Function) => {
55
const rootDir = await get('rootDir');
56
return val && typeof val === 'string'
57
? resolve(rootDir, val)
58
: await findWorkspaceDir(rootDir, { gitConfig: 'closest', try: true })
59
.catch(() => rootDir);
60
}
61
},
62
63
srcDir: {
64
$resolve: async (val: unknown, get: Function) => {
65
if (val && typeof val === 'string') {
66
return resolve(await get('rootDir'), val);
67
}
68
69
const rootDir = await get('rootDir');
70
const srcDir = resolve(rootDir, 'app');
71
72
if (!existsSync(srcDir)) {
73
return rootDir;
74
}
75
76
return srcDir;
77
}
78
}
79
}
80
```
81
82
### App Configuration Resolvers
83
84
Application-specific configuration with validation:
85
86
```typescript { .api }
87
const appResolvers = {
88
vue: {
89
transformAssetUrls: {
90
video: ['src', 'poster'],
91
source: ['src'],
92
img: ['src'],
93
image: ['xlink:href', 'href'],
94
use: ['xlink:href', 'href']
95
},
96
compilerOptions: {},
97
runtimeCompiler: {
98
$resolve: (val: unknown) => typeof val === 'boolean' ? val : false
99
},
100
propsDestructure: true,
101
config: {}
102
},
103
104
app: {
105
baseURL: {
106
$resolve: (val: unknown) => {
107
if (typeof val === 'string') {
108
return withTrailingSlash(val);
109
}
110
return '/';
111
}
112
},
113
114
buildAssetsDir: {
115
$resolve: (val: unknown, get: Function) => {
116
return val || '/_nuxt/';
117
}
118
}
119
}
120
}
121
```
122
123
### Build Configuration Resolvers
124
125
Build system configuration with defaults:
126
127
```typescript { .api }
128
const buildResolvers = {
129
builder: {
130
$resolve: (val: unknown) => {
131
if (val && typeof val === 'object' && 'bundle' in val) {
132
return val as NuxtBuilder;
133
}
134
135
const map = {
136
rspack: '@nuxt/rspack-builder',
137
vite: '@nuxt/vite-builder',
138
webpack: '@nuxt/webpack-builder'
139
};
140
141
if (typeof val === 'string' && val in map) {
142
return map[val as keyof typeof map];
143
}
144
145
return map.vite;
146
}
147
},
148
149
sourcemap: {
150
$resolve: async (val: unknown, get: Function) => {
151
if (typeof val === 'boolean') {
152
return { server: val, client: val };
153
}
154
155
if (val && typeof val === 'object') {
156
return {
157
server: val.server ?? true,
158
client: val.client ?? false
159
};
160
}
161
162
return { server: true, client: false };
163
}
164
}
165
}
166
```
167
168
## Schema Definition Utilities
169
170
### defineResolvers Helper
171
172
Utility function for creating type-safe configuration resolvers:
173
174
```typescript { .api }
175
function defineResolvers<C extends Partial<Resolvable<ConfigSchema>>>(config: C): any {
176
return config as any;
177
}
178
179
type Resolvable<Namespace> = keyof Exclude<NonNullable<Namespace>, boolean | string | (() => any)> extends string
180
? {
181
[K in keyof Namespace]: Partial<Resolvable<Namespace[K]>> | Resolvers<Namespace[K]>
182
} | Namespace
183
: Namespace | Resolvers<Namespace>
184
185
interface Resolvers<ReturnValue> {
186
$resolve: (val: unknown, get: <K extends KeysOf<ConfigSchema>>(key: K) => Promise<ReturnFromKey<ConfigSchema, K>>) => Awaitable<ReturnValue>
187
$schema?: InputObject['$schema']
188
$default?: ReturnValue
189
}
190
```
191
192
## Configuration Validation Patterns
193
194
### Custom Schema Definition
195
196
```typescript
197
import { defineResolvers } from '@nuxt/schema/utils/definition';
198
199
const myModuleResolvers = defineResolvers({
200
myModule: {
201
enabled: {
202
$resolve: (val) => val !== false,
203
$default: true,
204
$schema: {
205
type: 'boolean',
206
description: 'Enable the module'
207
}
208
},
209
210
apiKey: {
211
$resolve: (val) => {
212
if (typeof val !== 'string' || val.length === 0) {
213
throw new Error('API key is required and must be a non-empty string');
214
}
215
return val;
216
},
217
$schema: {
218
type: 'string',
219
description: 'API key for the service'
220
}
221
},
222
223
timeout: {
224
$resolve: (val) => {
225
const timeout = typeof val === 'number' ? val : 5000;
226
if (timeout < 0) {
227
throw new Error('Timeout must be a positive number');
228
}
229
return timeout;
230
},
231
$default: 5000,
232
$schema: {
233
type: 'number',
234
minimum: 0,
235
description: 'Request timeout in milliseconds'
236
}
237
}
238
}
239
});
240
```
241
242
### Conditional Resolution
243
244
```typescript
245
const conditionalResolvers = defineResolvers({
246
database: {
247
$resolve: async (val, get) => {
248
const isDev = await get('dev');
249
250
if (typeof val === 'object' && val !== null) {
251
return val;
252
}
253
254
// Default configuration based on environment
255
return {
256
host: isDev ? 'localhost' : process.env.DB_HOST,
257
port: isDev ? 5432 : parseInt(process.env.DB_PORT || '5432'),
258
name: isDev ? 'dev_db' : process.env.DB_NAME,
259
ssl: !isDev
260
};
261
}
262
},
263
264
features: {
265
analytics: {
266
$resolve: async (val, get) => {
267
const isDev = await get('dev');
268
269
// Disable analytics in development by default
270
if (isDev && val === undefined) {
271
return false;
272
}
273
274
return val !== false;
275
}
276
}
277
}
278
});
279
```
280
281
### Schema Validation
282
283
```typescript
284
import type { SchemaDefinition } from '@nuxt/schema';
285
286
const moduleSchema: SchemaDefinition = {
287
type: 'object',
288
properties: {
289
myModule: {
290
type: 'object',
291
properties: {
292
enabled: {
293
type: 'boolean',
294
default: true,
295
description: 'Enable the module'
296
},
297
apiKey: {
298
type: 'string',
299
minLength: 1,
300
description: 'API key for the service'
301
},
302
timeout: {
303
type: 'number',
304
minimum: 0,
305
default: 5000,
306
description: 'Request timeout in milliseconds'
307
},
308
endpoints: {
309
type: 'object',
310
properties: {
311
api: { type: 'string', format: 'uri' },
312
auth: { type: 'string', format: 'uri' }
313
},
314
required: ['api']
315
}
316
},
317
required: ['apiKey']
318
}
319
}
320
};
321
```
322
323
## Runtime Schema Usage
324
325
### Configuration Resolution in Modules
326
327
```typescript
328
export default defineNuxtModule({
329
meta: {
330
name: 'my-module',
331
configKey: 'myModule'
332
},
333
334
schema: {
335
enabled: { type: 'boolean', default: true },
336
apiKey: { type: 'string' },
337
timeout: { type: 'number', default: 5000, minimum: 0 }
338
},
339
340
setup(options, nuxt) {
341
// Options are already resolved and validated by schema
342
if (!options.enabled) return;
343
344
// Use validated configuration
345
console.log(`Module enabled with timeout: ${options.timeout}ms`);
346
347
// Add runtime configuration
348
nuxt.options.runtimeConfig.myModule = {
349
apiKey: options.apiKey
350
};
351
}
352
});
353
```
354
355
### Schema Extension
356
357
```typescript
358
// Extend existing schema
359
export default defineNuxtModule({
360
setup(options, nuxt) {
361
// Extend the Nuxt config schema
362
nuxt.options.$schema = nuxt.options.$schema || {};
363
364
// Add module-specific schema
365
nuxt.options.$schema.properties = {
366
...nuxt.options.$schema.properties,
367
myModule: {
368
type: 'object',
369
properties: {
370
customOption: {
371
type: 'string',
372
default: 'default-value'
373
}
374
}
375
}
376
};
377
}
378
});
379
```
380
381
### Validation Helpers
382
383
```typescript
384
import { validateConfig } from 'untyped';
385
386
export function validateNuxtConfig(config: any, schema: SchemaDefinition) {
387
const result = validateConfig(config, schema);
388
389
if (result.errors && result.errors.length > 0) {
390
const errorMessages = result.errors.map(error =>
391
`${error.path}: ${error.message}`
392
).join('\n');
393
394
throw new Error(`Configuration validation failed:\n${errorMessages}`);
395
}
396
397
return result.config;
398
}
399
```
400
401
### Dynamic Schema Generation
402
403
```typescript
404
export function createModuleSchema(options: {
405
required?: string[],
406
optional?: string[],
407
types?: Record<string, any>
408
}) {
409
const properties: Record<string, any> = {};
410
411
// Add required properties
412
options.required?.forEach(prop => {
413
properties[prop] = {
414
type: options.types?.[prop] || 'string',
415
description: `Required ${prop} configuration`
416
};
417
});
418
419
// Add optional properties
420
options.optional?.forEach(prop => {
421
properties[prop] = {
422
type: options.types?.[prop] || 'string',
423
description: `Optional ${prop} configuration`
424
};
425
});
426
427
return {
428
type: 'object',
429
properties,
430
required: options.required || []
431
};
432
}
433
```
434
435
The schema validation system ensures that all Nuxt configuration is properly validated and resolved, providing type safety and clear error messages when configuration issues are detected.