or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

compatibility.mdcomponents.mdconfiguration-types.mdcore-types.mdhooks.mdindex.mdmodules.mdruntime-config.mdschema-validation.md

schema-validation.mddocs/

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.