Advanced Usage

Custom Filters, Sorts & Includes

Build reusable filter, sort, and include classes implementing Filter, Sort, and Include interfaces.

When callback functions (AllowedFilter.callback, AllowedSort.callback, AllowedInclude.callback) need to be reused across multiple models or endpoints, you can create dedicated classes implementing the Filter, Sort, or Include interfaces from @eienjs/adonisjs-api-query/types.

Custom Filter Class

Implement the Filter<Model> interface with a handle(query, value, property) method and register it via AllowedFilter.custom():

import type { LucidModel, ModelQueryBuilderContract } from '@adonisjs/lucid/types/model';
import type { StrictValuesWithoutRaw } from '@adonisjs/lucid/types/querybuilder';
import type { Filter } from '@eienjs/adonisjs-api-query/types';

export class DateRangeFilter<Model extends LucidModel> implements Filter<Model> {
  public handle(
    query: ModelQueryBuilderContract<Model, InstanceType<Model>>,
    value: StrictValuesWithoutRaw | null,
    property: string,
  ): void {
    if (!Array.isArray(value) || value.length !== 2) {
      return;
    }

    const [startDate, endDate] = value as [string, string];
    void query.whereBetween(property, [startDate, endDate]);
  }
}

Custom Sort Class

Implement the Sort<Model> interface with a handle(query, isDescending, property) method and register it via AllowedSort.custom():

import type { LucidModel, ModelQueryBuilderContract } from '@adonisjs/lucid/types/model';
import type { Sort } from '@eienjs/adonisjs-api-query/types';

export class StringLengthSort<Model extends LucidModel> implements Sort<Model> {
  public handle(
    query: ModelQueryBuilderContract<Model, InstanceType<Model>>,
    isDescending: boolean,
    property: string,
  ): void {
    const direction = isDescending ? 'DESC' : 'ASC';
    void query.orderByRaw(`LENGTH(??) ${direction}`, [property]);
  }
}

Custom Include Class

Implement the Include<ParentModel> interface with a handle(query, include) method and register it via AllowedInclude.custom():

import type { LucidModel, ModelQueryBuilderContract } from '@adonisjs/lucid/types/model';
import type { ExtractModelRelations } from '@adonisjs/lucid/types/relations';
import type { Include } from '@eienjs/adonisjs-api-query/types';

export class PublishedRelationInclude<ParentModel extends LucidModel> implements Include<ParentModel> {
  public handle(
    query: ModelQueryBuilderContract<ParentModel, InstanceType<ParentModel>>,
    include: string,
  ): void {
    void query.preload(include as ExtractModelRelations<InstanceType<ParentModel>>, (subQuery) => {
      void subQuery.where('isPublished', true);
    });
  }
}