or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

expression-builders.mdgraph-operations.mdindex.mdmodel-definition.mdquery-building.mdrelationships.mdtransactions.mdutilities.mdvalidation.md

validation.mddocs/

0

# Validation and Error Handling

1

2

JSON Schema validation system and comprehensive error types for database constraints.

3

4

## Capabilities

5

6

### Validation Error

7

8

Error thrown during model validation with detailed error information.

9

10

```javascript { .api }

11

/**

12

* Validation error with detailed error information

13

*/

14

class ValidationError extends Error {

15

constructor(args: CreateValidationErrorArgs);

16

17

/** HTTP status code (typically 400) */

18

statusCode: number;

19

20

/** Error message */

21

message: string;

22

23

/** Detailed validation error data */

24

data?: ErrorHash | any;

25

26

/** Error type identifier */

27

type: ValidationErrorType | string;

28

29

/** Model class that failed validation */

30

modelClass?: typeof Model;

31

}

32

33

interface CreateValidationErrorArgs {

34

statusCode?: number;

35

message?: string;

36

data?: ErrorHash | any;

37

type: ValidationErrorType | string;

38

}

39

40

type ValidationErrorType =

41

| 'ModelValidation'

42

| 'RelationExpression'

43

| 'UnallowedRelation'

44

| 'InvalidGraph';

45

46

interface ErrorHash {

47

[propertyName: string]: ValidationErrorItem[];

48

}

49

50

interface ValidationErrorItem {

51

message: string;

52

keyword: string;

53

params: object;

54

}

55

```

56

57

**Usage Examples:**

58

59

```javascript

60

const { ValidationError } = require('objection');

61

62

try {

63

await Person.query().insert({

64

firstName: '', // Required field

65

age: -5, // Invalid age

66

email: 'invalid-email' // Invalid format

67

});

68

} catch (error) {

69

if (error instanceof ValidationError) {

70

console.log('Validation failed:', error.message);

71

console.log('Status code:', error.statusCode); // 400

72

console.log('Error type:', error.type); // 'ModelValidation'

73

console.log('Field errors:', error.data);

74

// {

75

// firstName: [{ message: 'should have required property firstName', keyword: 'required', params: {} }],

76

// age: [{ message: 'should be >= 0', keyword: 'minimum', params: { limit: 0 } }],

77

// email: [{ message: 'should match format "email"', keyword: 'format', params: { format: 'email' } }]

78

// }

79

}

80

}

81

```

82

83

### Not Found Error

84

85

Error thrown when queries return no results but results are expected.

86

87

```javascript { .api }

88

/**

89

* Error for operations that expect to find records but don't

90

*/

91

class NotFoundError extends Error {

92

constructor(args: CreateNotFoundErrorArgs);

93

94

/** HTTP status code (typically 404) */

95

statusCode: number;

96

97

/** Additional error data */

98

data?: any;

99

100

/** Error type identifier */

101

type: 'NotFound';

102

103

/** Model class that was not found */

104

modelClass?: typeof Model;

105

}

106

107

interface CreateNotFoundErrorArgs {

108

statusCode?: number;

109

message?: string;

110

data?: any;

111

[key: string]: any;

112

}

113

```

114

115

**Usage Examples:**

116

117

```javascript

118

const { NotFoundError } = require('objection');

119

120

try {

121

const person = await Person.query()

122

.findById(999)

123

.throwIfNotFound();

124

} catch (error) {

125

if (error instanceof NotFoundError) {

126

console.log('Person not found');

127

console.log('Status code:', error.statusCode); // 404

128

console.log('Error type:', error.type); // 'NotFound'

129

}

130

}

131

132

// Custom not found handling

133

try {

134

const person = await Person.query()

135

.where('email', 'nonexistent@example.com')

136

.first()

137

.throwIfNotFound({ message: 'User with that email not found' });

138

} catch (error) {

139

console.log(error.message); // 'User with that email not found'

140

}

141

```

142

143

### AJV Validator

144

145

JSON Schema validator using the AJV library for comprehensive validation.

146

147

```javascript { .api }

148

/**

149

* JSON Schema validator using AJV

150

*/

151

class AjvValidator extends Validator {

152

constructor(config: AjvConfig);

153

154

/** Validate model data against JSON schema */

155

validate(args: ValidatorArgs): object;

156

157

/** Pre-validation hook */

158

beforeValidate(args: ValidatorArgs): void;

159

160

/** Post-validation hook */

161

afterValidate(args: ValidatorArgs): void;

162

}

163

164

interface AjvConfig {

165

onCreateAjv?: (ajv: Ajv) => void;

166

options?: AjvOptions;

167

}

168

169

interface ValidatorArgs {

170

ctx: ValidatorContext;

171

model: Model;

172

json: object;

173

options: ModelOptions;

174

}

175

176

interface ValidatorContext {

177

[key: string]: any;

178

}

179

```

180

181

**Usage Examples:**

182

183

```javascript

184

const { AjvValidator } = require('objection');

185

186

// Custom validator configuration

187

class Person extends Model {

188

static get tableName() {

189

return 'persons';

190

}

191

192

static createValidator() {

193

return new AjvValidator({

194

onCreateAjv: (ajv) => {

195

// Add custom formats

196

ajv.addFormat('phone', /^\d{3}-\d{3}-\d{4}$/);

197

},

198

options: {

199

allErrors: true,

200

verbose: true

201

}

202

});

203

}

204

205

static get jsonSchema() {

206

return {

207

type: 'object',

208

required: ['firstName', 'lastName', 'phone'],

209

properties: {

210

id: { type: 'integer' },

211

firstName: { type: 'string', minLength: 1, maxLength: 255 },

212

lastName: { type: 'string', minLength: 1, maxLength: 255 },

213

phone: { type: 'string', format: 'phone' },

214

age: { type: 'integer', minimum: 0, maximum: 200 },

215

email: { type: 'string', format: 'email' }

216

}

217

};

218

}

219

}

220

```

221

222

### Base Validator

223

224

Base validator class for custom validation implementations.

225

226

```javascript { .api }

227

/**

228

* Base validator class

229

*/

230

class Validator {

231

/** Pre-validation hook */

232

beforeValidate(args: ValidatorArgs): void;

233

234

/** Main validation method */

235

validate(args: ValidatorArgs): object;

236

237

/** Post-validation hook */

238

afterValidate(args: ValidatorArgs): void;

239

}

240

```

241

242

**Usage Examples:**

243

244

```javascript

245

const { Validator } = require('objection');

246

247

// Custom validator implementation

248

class CustomValidator extends Validator {

249

validate({ model, json, options }) {

250

const errors = {};

251

252

// Custom validation logic

253

if (json.firstName && json.firstName.length < 2) {

254

errors.firstName = [{

255

message: 'First name must be at least 2 characters',

256

keyword: 'minLength',

257

params: { limit: 2 }

258

}];

259

}

260

261

if (json.age && json.age < 0) {

262

errors.age = [{

263

message: 'Age cannot be negative',

264

keyword: 'minimum',

265

params: { limit: 0 }

266

}];

267

}

268

269

if (Object.keys(errors).length > 0) {

270

throw new ValidationError({

271

type: 'ModelValidation',

272

data: errors

273

});

274

}

275

276

return json;

277

}

278

}

279

280

class Person extends Model {

281

static createValidator() {

282

return new CustomValidator();

283

}

284

}

285

```

286

287

### Model Validation Hooks

288

289

Validation lifecycle hooks available on model instances.

290

291

```javascript { .api }

292

/**

293

* Pre-validation hook

294

* @param jsonSchema - JSON schema for validation

295

* @param json - Data to validate

296

* @param options - Validation options

297

* @returns Modified schema or original schema

298

*/

299

$beforeValidate(jsonSchema: object, json: object, options: ModelOptions): object;

300

301

/**

302

* Main validation method

303

* @param json - Data to validate (optional, uses model's own data if not provided)

304

* @param options - Validation options

305

* @returns Validated data

306

*/

307

$validate(json?: object, options?: ModelOptions): object;

308

309

/**

310

* Post-validation hook

311

* @param json - Validated data

312

* @param options - Validation options

313

*/

314

$afterValidate(json: object, options: ModelOptions): void;

315

```

316

317

**Usage Examples:**

318

319

```javascript

320

class Person extends Model {

321

$beforeValidate(jsonSchema, json, options) {

322

// Modify schema based on context

323

if (options.skipEmailValidation) {

324

const schema = { ...jsonSchema };

325

delete schema.properties.email.format;

326

return schema;

327

}

328

return jsonSchema;

329

}

330

331

$afterValidate(json, options) {

332

// Custom post-validation logic

333

if (json.firstName && json.lastName) {

334

this.fullName = `${json.firstName} ${json.lastName}`;

335

}

336

}

337

}

338

339

// Use validation with options

340

const person = Person.fromJson({

341

firstName: 'John',

342

lastName: 'Doe',

343

email: 'invalid-email'

344

}, { skipEmailValidation: true });

345

```

346

347

### Database Errors

348

349

Database constraint violation errors from the db-errors package.

350

351

```javascript { .api }

352

/**

353

* Base database error

354

*/

355

class DBError extends Error {

356

nativeError: Error;

357

client: string;

358

}

359

360

/**

361

* Unique constraint violation

362

*/

363

class UniqueViolationError extends ConstraintViolationError {

364

columns: string[];

365

table: string;

366

constraint: string;

367

}

368

369

/**

370

* NOT NULL constraint violation

371

*/

372

class NotNullViolationError extends ConstraintViolationError {

373

column: string;

374

table: string;

375

}

376

377

/**

378

* Foreign key constraint violation

379

*/

380

class ForeignKeyViolationError extends ConstraintViolationError {

381

table: string;

382

constraint: string;

383

}

384

385

/**

386

* Base constraint violation error

387

*/

388

class ConstraintViolationError extends DBError {}

389

390

/**

391

* Check constraint violation

392

*/

393

class CheckViolationError extends ConstraintViolationError {

394

table: string;

395

constraint: string;

396

}

397

398

/**

399

* Data-related database error

400

*/

401

class DataError extends DBError {}

402

```

403

404

**Usage Examples:**

405

406

```javascript

407

const {

408

UniqueViolationError,

409

NotNullViolationError,

410

ForeignKeyViolationError

411

} = require('objection');

412

413

try {

414

await Person.query().insert({

415

email: 'existing@example.com' // Duplicate email

416

});

417

} catch (error) {

418

if (error instanceof UniqueViolationError) {

419

console.log('Duplicate value for:', error.columns); // ['email']

420

console.log('In table:', error.table); // 'persons'

421

console.log('Constraint:', error.constraint); // 'persons_email_unique'

422

} else if (error instanceof NotNullViolationError) {

423

console.log('Missing required field:', error.column);

424

} else if (error instanceof ForeignKeyViolationError) {

425

console.log('Invalid foreign key reference');

426

}

427

}

428

```

429

430

### Validation Options

431

432

Options for controlling validation behavior.

433

434

```javascript { .api }

435

interface ModelOptions {

436

/** Skip JSON schema validation */

437

skipValidation?: boolean;

438

439

/** Perform partial validation (PATCH semantics) */

440

patch?: boolean;

441

442

/** Reference to old model data for comparison */

443

old?: object;

444

}

445

```

446

447

**Usage Examples:**

448

449

```javascript

450

// Skip validation for performance

451

const person = Person.fromJson(data, { skipValidation: true });

452

453

// Patch validation (only validate provided fields)

454

await Person.query()

455

.findById(1)

456

.patch({ age: 30 }, { patch: true });

457

458

// Validation with old data reference

459

const updated = await person.$query()

460

.patch(newData, { old: person.toJSON() });

461

```

462

463

## Types

464

465

```typescript { .api }

466

type ValidationErrorType =

467

| 'ModelValidation'

468

| 'RelationExpression'

469

| 'UnallowedRelation'

470

| 'InvalidGraph';

471

472

interface ValidationErrorItem {

473

message: string;

474

keyword: string;

475

params: object;

476

}

477

478

interface ErrorHash {

479

[propertyName: string]: ValidationErrorItem[];

480

}

481

482

interface ValidatorContext {

483

[key: string]: any;

484

}

485

486

interface ValidatorArgs {

487

ctx: ValidatorContext;

488

model: Model;

489

json: object;

490

options: ModelOptions;

491

}

492

493

interface AjvConfig {

494

onCreateAjv?: (ajv: Ajv) => void;

495

options?: AjvOptions;

496

}

497

498

interface ModelOptions {

499

patch?: boolean;

500

skipValidation?: boolean;

501

old?: object;

502

}

503

```