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

utilities.mddocs/

0

# Utilities and Plugins

1

2

Utility functions for column mapping, mixins, and extending model functionality.

3

4

## Capabilities

5

6

### Snake Case Mappers

7

8

Utility functions for converting between camelCase and snake_case column names.

9

10

```javascript { .api }

11

/**

12

* Create column name mappers for snake_case conversion

13

* @param options - Snake case conversion options

14

* @returns Column name mappers object

15

*/

16

function snakeCaseMappers(options?: SnakeCaseMappersOptions): ColumnNameMappers;

17

18

interface SnakeCaseMappersOptions {

19

/** Convert to uppercase (SCREAMING_SNAKE_CASE) */

20

upperCase?: boolean;

21

22

/** Add underscore before digits */

23

underscoreBeforeDigits?: boolean;

24

25

/** Add underscore between consecutive uppercase letters */

26

underscoreBetweenUppercaseLetters?: boolean;

27

}

28

29

interface ColumnNameMappers {

30

/** Parse database column names to model property names */

31

parse(json: object): object;

32

33

/** Format model property names to database column names */

34

format(json: object): object;

35

}

36

```

37

38

**Usage Examples:**

39

40

```javascript

41

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

42

43

class Person extends Model {

44

static get tableName() {

45

return 'persons';

46

}

47

48

static get columnNameMappers() {

49

return snakeCaseMappers();

50

}

51

}

52

53

// Now you can use camelCase in your models

54

const person = await Person.query().insert({

55

firstName: 'John', // Maps to first_name in database

56

lastName: 'Doe', // Maps to last_name in database

57

phoneNumber: '555-1234' // Maps to phone_number in database

58

});

59

60

// Custom options

61

const mappers = snakeCaseMappers({

62

upperCase: true, // FIRST_NAME instead of first_name

63

underscoreBeforeDigits: true, // address2 -> address_2

64

underscoreBetweenUppercaseLetters: true // XMLParser -> x_m_l_parser

65

});

66

67

class CustomModel extends Model {

68

static get columnNameMappers() {

69

return mappers;

70

}

71

}

72

```

73

74

### Knex Snake Case Mappers

75

76

Knex-level mappers for global snake_case conversion.

77

78

```javascript { .api }

79

/**

80

* Create Knex mappers for snake_case conversion

81

* @param options - Snake case conversion options

82

* @returns Knex mappers object

83

*/

84

function knexSnakeCaseMappers(options?: SnakeCaseMappersOptions): KnexMappers;

85

86

interface KnexMappers {

87

/** Wrap identifiers (table/column names) for queries */

88

wrapIdentifier(identifier: string, origWrap: (str: string) => string): string;

89

90

/** Process response data after query execution */

91

postProcessResponse(response: any): any;

92

}

93

```

94

95

**Usage Examples:**

96

97

```javascript

98

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

99

const Knex = require('knex');

100

101

// Configure Knex with snake case mappers

102

const knex = Knex({

103

client: 'postgresql',

104

connection: connectionConfig,

105

...knexSnakeCaseMappers()

106

});

107

108

// All queries will automatically convert between camelCase and snake_case

109

Model.knex(knex);

110

111

// Model properties in camelCase

112

class Person extends Model {

113

static get tableName() {

114

return 'persons'; // Actually maps to 'persons' table with snake_case columns

115

}

116

}

117

118

// Usage is the same, but conversion happens automatically

119

const person = await Person.query().insert({

120

firstName: 'John', // Automatically becomes first_name in SQL

121

lastName: 'Doe', // Automatically becomes last_name in SQL

122

dateOfBirth: new Date() // Automatically becomes date_of_birth in SQL

123

});

124

```

125

126

### Identifier Mapping

127

128

Advanced identifier mapping functionality.

129

130

```javascript { .api }

131

/**

132

* Create identifier mapping configuration for Knex

133

* @param options - Mapping options

134

* @returns Knex configuration object

135

*/

136

function knexIdentifierMapping(options?: IdentifierMappingOptions): object;

137

138

interface IdentifierMappingOptions {

139

upperCase?: boolean;

140

underscoreBeforeDigits?: boolean;

141

underscoreBetweenUppercaseLetters?: boolean;

142

}

143

```

144

145

### Model Mixins

146

147

Plugin system for extending model functionality.

148

149

```javascript { .api }

150

/**

151

* Apply plugins to a model class

152

* @param modelClass - Model class to extend

153

* @param plugins - Plugins to apply

154

* @returns Extended model class

155

*/

156

function mixin<T extends typeof Model>(

157

modelClass: T,

158

...plugins: Plugin[]

159

): T;

160

161

/**

162

* Compose multiple plugins into a single plugin

163

* @param plugins - Plugins to compose

164

* @returns Composed plugin

165

*/

166

function compose(...plugins: Plugin[]): Plugin;

167

168

/**

169

* Plugin function type

170

*/

171

type Plugin = <T extends typeof Model>(modelClass: T) => T;

172

```

173

174

**Usage Examples:**

175

176

```javascript

177

const { mixin, compose } = require('objection');

178

179

// Define plugins

180

const timestampPlugin = (Model) => {

181

return class extends Model {

182

$beforeInsert() {

183

this.createdAt = new Date();

184

this.updatedAt = new Date();

185

}

186

187

$beforeUpdate() {

188

this.updatedAt = new Date();

189

}

190

};

191

};

192

193

const softDeletePlugin = (Model) => {

194

return class extends Model {

195

static get modifiers() {

196

return {

197

...super.modifiers,

198

notDeleted: (query) => query.whereNull('deletedAt')

199

};

200

}

201

202

$beforeDelete() {

203

return this.$query().patch({ deletedAt: new Date() });

204

}

205

};

206

};

207

208

// Apply single plugin

209

const PersonWithTimestamps = mixin(Person, timestampPlugin);

210

211

// Apply multiple plugins

212

const PersonWithPlugins = mixin(Person, timestampPlugin, softDeletePlugin);

213

214

// Compose plugins

215

const combinedPlugin = compose(timestampPlugin, softDeletePlugin);

216

const PersonWithCombined = mixin(Person, combinedPlugin);

217

218

// Create model with plugins applied

219

class EnhancedPerson extends mixin(Model, timestampPlugin, softDeletePlugin) {

220

static get tableName() {

221

return 'persons';

222

}

223

}

224

```

225

226

### Model Traversal

227

228

Utilities for traversing model graphs and relationships.

229

230

```javascript { .api }

231

/**

232

* Traverse model instances and their relations

233

* @param models - Model instance(s) to traverse

234

* @param traverser - Function called for each model

235

*/

236

static traverse(

237

models: Model | Model[],

238

traverser: TraverserFunction

239

): void;

240

241

/**

242

* Traverse model instances with filtering

243

* @param filterConstructor - Only traverse models of this type

244

* @param models - Model instance(s) to traverse

245

* @param traverser - Function called for each model

246

*/

247

static traverse(

248

filterConstructor: typeof Model,

249

models: Model | Model[],

250

traverser: TraverserFunction

251

): void;

252

253

/**

254

* Async version of traverse

255

*/

256

static traverseAsync(

257

models: Model | Model[],

258

traverser: TraverserFunction

259

): Promise<void>;

260

261

type TraverserFunction = (

262

model: Model,

263

parentModel?: Model,

264

relationName?: string

265

) => void;

266

```

267

268

**Usage Examples:**

269

270

```javascript

271

// Traverse all models in a graph

272

const people = await Person.query()

273

.withGraphFetched('[pets, movies.actors]');

274

275

Person.traverse(people, (model, parent, relationName) => {

276

console.log(`Found ${model.constructor.name}:`, model.id);

277

if (parent) {

278

console.log(` Child of ${parent.constructor.name} via ${relationName}`);

279

}

280

});

281

282

// Traverse only specific model types

283

Person.traverse(Pet, people, (pet) => {

284

console.log('Found pet:', pet.name);

285

});

286

287

// Async traversal for database operations

288

await Person.traverseAsync(people, async (model) => {

289

if (model instanceof Person) {

290

await model.$query().patch({ lastAccessed: new Date() });

291

}

292

});

293

294

// Instance-level traversal

295

const person = await Person.query()

296

.findById(1)

297

.withGraphFetched('pets');

298

299

person.$traverse((model) => {

300

model.accessed = true;

301

});

302

```

303

304

### Initialization

305

306

Initialize models with database connections and metadata.

307

308

```javascript { .api }

309

/**

310

* Initialize model classes with Knex instance and fetch metadata

311

* @param knex - Knex instance to bind

312

* @param modelClasses - Model classes to initialize

313

* @returns Promise resolving when initialization is complete

314

*/

315

function initialize(

316

knex: Knex,

317

modelClasses: Array<typeof Model>

318

): Promise<void>;

319

320

/**

321

* Initialize with default Knex instance

322

* @param modelClasses - Model classes to initialize

323

* @returns Promise resolving when initialization is complete

324

*/

325

function initialize(modelClasses: Array<typeof Model>): Promise<void>;

326

```

327

328

**Usage Examples:**

329

330

```javascript

331

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

332

const Knex = require('knex');

333

334

const knex = Knex({

335

client: 'postgresql',

336

connection: connectionConfig

337

});

338

339

// Initialize models

340

await initialize(knex, [Person, Pet, Movie]);

341

342

// Models are now ready to use

343

const people = await Person.query();

344

```

345

346

### Table Metadata

347

348

Utilities for fetching and caching table metadata.

349

350

```javascript { .api }

351

/**

352

* Get cached table metadata

353

* @param options - Metadata options

354

* @returns Table metadata object

355

*/

356

static tableMetadata(options?: TableMetadataOptions): TableMetadata;

357

358

/**

359

* Fetch fresh table metadata from database

360

* @param options - Fetch options

361

* @returns Promise resolving to table metadata

362

*/

363

static fetchTableMetadata(options?: FetchTableMetadataOptions): Promise<TableMetadata>;

364

365

interface TableMetadata {

366

columns: string[];

367

}

368

369

interface TableMetadataOptions {

370

table?: string;

371

}

372

373

interface FetchTableMetadataOptions {

374

knex?: Knex;

375

force?: boolean;

376

table?: string;

377

}

378

```

379

380

**Usage Examples:**

381

382

```javascript

383

// Get cached metadata

384

const metadata = Person.tableMetadata();

385

console.log('Columns:', metadata.columns);

386

387

// Fetch fresh metadata

388

const freshMetadata = await Person.fetchTableMetadata({ force: true });

389

console.log('Fresh columns:', freshMetadata.columns);

390

391

// Fetch metadata for specific table

392

const petMetadata = await Person.fetchTableMetadata({ table: 'pets' });

393

```

394

395

### Class Utilities

396

397

Low-level utilities for class manipulation and inheritance.

398

399

```javascript { .api }

400

/**

401

* Set up inheritance between classes (used internally)

402

* @param Child - Child class

403

* @param Parent - Parent class

404

*/

405

function inherit(Child: Function, Parent: Function): void;

406

```

407

408

## Types

409

410

```typescript { .api }

411

interface SnakeCaseMappersOptions {

412

upperCase?: boolean;

413

underscoreBeforeDigits?: boolean;

414

underscoreBetweenUppercaseLetters?: boolean;

415

}

416

417

interface ColumnNameMappers {

418

parse(json: object): object;

419

format(json: object): object;

420

}

421

422

interface KnexMappers {

423

wrapIdentifier(identifier: string, origWrap: (str: string) => string): string;

424

postProcessResponse(response: any): any;

425

}

426

427

type Plugin = <T extends typeof Model>(modelClass: T) => T;

428

429

type TraverserFunction = (

430

model: Model,

431

parentModel?: Model,

432

relationName?: string

433

) => void;

434

435

interface TableMetadata {

436

columns: string[];

437

}

438

439

interface TableMetadataOptions {

440

table?: string;

441

}

442

443

interface FetchTableMetadataOptions {

444

knex?: Knex;

445

force?: boolean;

446

table?: string;

447

}

448

```