# Custom query builder (extending the query builder)

You can extend the QueryBuilder returned by query(), relatedQuery(), $relatedQuery() and $query() methods (and all other methods that create a QueryBuilder) by setting the model class's static QueryBuilder property.

// MyQueryBuilder.js
const { QueryBuilder } = require('objection');

class MyQueryBuilder extends QueryBuilder {
  myCustomMethod(something) {
    doSomething(something);
    return this;
  }
}

// Person.js
const { MyQueryBuilder } = require('./MyQueryBuilder');

class Person extends Model {
  static get QueryBuilder() {
    return MyQueryBuilder;
  }
}

Now you can do this:

await Person.query()
  .where('id', 1)
  .myCustomMethod(1)
  .where('foo', 'bar');

If you want to set the custom query builder for all model classes you can just set the QueryBuilder property of the Model base class. A cleaner option would be to create your own Model subclass, set its QueryBuilder property and inherit all your models from the custom Model class.

// BaseModel.js
const { MyQueryBuilder } = require('./MyQueryBuilder');

class BaseModel extends Model {
  static get QueryBuilder() {
    return MyQueryBuilder;
  }
}

// Person.js
const { BaseModel } = require('./BaseModel');

// Person now uses MyQueryBuilder
class Person extends BaseModel {}

Whenever a QueryBuilder instance is created it is done by calling the static query() method. If you don't need to add methods, but simply modify the query, you can override the query().

class BaseModel extends Model {
  static query(...args) {
    const query = super.query(...args);

    // Somehow modify the query.
    return query.runAfter(result => {
      console.log(this.name, 'got result', result);
      return result;
    });
  }
}

TIP

TIP: Consider using modifiers instead of extending the query builder. You can often achieve the same flexibility with both.

# Extending the query builder in typescript

With typescript, you need to add some extra type properties for the custom query builder. These are necessary until typescript fully supports our use case. The good news is that you only need to define them once for the shared BaseModel. If you don't already have one, it's time to create it.

import { Model, Page } from 'objection';

class MyQueryBuilder<M extends Model, R = M[]> extends QueryBuilder<M, R> {
  // These are necessary. You can just copy-paste them and change the
  // name of the query builder class.
  ArrayQueryBuilderType!: MyQueryBuilder<M, M[]>;
  SingleQueryBuilderType!: MyQueryBuilder<M, M>;
  NumberQueryBuilderType!: MyQueryBuilder<M, number>;
  PageQueryBuilderType!: MyQueryBuilder<M, Page<M>>;

  myCustomMethod(something: number): this {
    doSomething(something);
    return this;
  }
}

class BaseModel extends Model {
  // Both of these are needed.
  QueryBuilderType!: MyQueryBuilder<this>;
  static QueryBuilder = MyQueryBuilder;
}

Now all models you inherit from BaseModel use MyQueryBuilder as a query builder.

class Person extends BaseModel {
  static tableName = 'persons';
}

await Person.query()
  .where('id', 1)
  .myCustomMethod(1)
  .where('foo', 'bar');

TIP

TIP: Consider using modifiers instead of extending the query builder. You can often achieve the same flexibility with both.