Skip to main content
The adapter-commons package provides shared utilities, base classes, and types used by all Feathers database adapters. This is typically used when building custom database adapters.

Installation

npm install @feathersjs/adapter-commons

AdapterBase

Abstract base class that all database adapters extend from.
import { AdapterBase } from '@feathersjs/adapter-commons'

class MyAdapter<Result, Data, ServiceParams, PatchData> extends AdapterBase<
  Result,
  Data, 
  PatchData,
  ServiceParams,
  Options
> {
  // Implement abstract methods
  async _find(params) { /* ... */ }
  async _get(id, params) { /* ... */ }
  async _create(data, params) { /* ... */ }
  async _update(id, data, params) { /* ... */ }
  async _patch(id, data, params) { /* ... */ }
  async _remove(id, params) { /* ... */ }
}

Constructor

options
AdapterServiceOptions
required
Base adapter options
id
string
default:"id"
Name of the id field property
paginate
PaginationParams
Pagination configuration with default and max values
multi
boolean | string[]
Allow multiple updates. true, false, or array of method names
events
string[]
Custom event names (deprecated, use service registration events option)
filters
FilterSettings
Custom query filters with converter functions
operators
string[]
Additional query operators to allow

Properties

id

The name of the id property.
const idField = adapter.id // 'id' or '_id'

events

List of custom event names.
const events = adapter.events

options

The full adapter options object.
const options = adapter.options

Methods

getOptions

Get the merged options for a service call, combining base options with params.
const options = adapter.getOptions(params)
params
ServiceParams
required
Service call parameters
result
Options
Merged options including params.adapter overrides

allowsMulti

Check if a method allows multiple updates.
const allowed = adapter.allowsMulti(method, params)
method
string
required
The method name (e.g., ‘create’, ‘patch’, ‘remove’)
params
ServiceParams
Service call parameters
result
boolean
Whether multiple updates are allowed for this method

sanitizeQuery

Convert and validate query parameters.
const query = await adapter.sanitizeQuery(params)
params
ServiceParams
Service call parameters with query
result
Query
Sanitized query with converted values

Abstract Methods

These methods must be implemented by the adapter:
abstract _find(params?: ServiceParams): Promise<Paginated<Result> | Result[]>
abstract _get(id: Id, params?: ServiceParams): Promise<Result>
abstract _create(data: Data | Data[], params?: ServiceParams): Promise<Result | Result[]>
abstract _update(id: Id, data: Data, params?: ServiceParams): Promise<Result>
abstract _patch(id: Id | null, data: PatchData, params?: ServiceParams): Promise<Result | Result[]>
abstract _remove(id: Id | null, params?: ServiceParams): Promise<Result | Result[]>

Query Filtering

filterQuery

Separate special query parameters from regular query.
import { filterQuery } from '@feathersjs/adapter-commons'

const { query, filters } = filterQuery(params.query, options)
query
Query
required
The incoming query object
options
FilterQueryOptions
filters
FilterSettings
Custom filter definitions
operators
string[]
Allowed query operators
paginate
PaginationParams
Pagination configuration
result
{ query: Query, filters: object }
Separated query and filters object
query
Query
Query without special parameters
filters
object
Object with $limit, $skip, $sort, $select, $or, $and

getLimit

Calculate the appropriate limit based on pagination settings.
import { getLimit } from '@feathersjs/adapter-commons'

const limit = getLimit(requestedLimit, paginate)
limit
any
required
The requested limit value
paginate
PaginationParams
Pagination configuration
result
number
The calculated limit (respecting max if set)

OPERATORS

Default query operators allowed by adapters.
import { OPERATORS } from '@feathersjs/adapter-commons'

console.log(OPERATORS)
// ['$in', '$nin', '$lt', '$lte', '$gt', '$gte', '$ne', '$or']

FILTERS

Default special query parameter converters.
import { FILTERS } from '@feathersjs/adapter-commons'

console.log(Object.keys(FILTERS))
// ['$skip', '$sort', '$limit', '$select', '$or', '$and']

Sorting

sorter

Create an in-memory sorting function from a $sort object.
import { sorter } from '@feathersjs/adapter-commons'

const sortFn = sorter({ name: 1, age: -1 })
const sorted = items.sort(sortFn)
$sort
{ [key: string]: 1 | -1 }
required
Sort specification object
result
(a: any, b: any) => number
Comparator function for Array.sort()

compare

Compare two values with proper type handling.
import { compare } from '@feathersjs/adapter-commons'

const result = compare(value1, value2)
// Returns -1, 0, or 1
a
any
required
First value
b
any
required
Second value
compareStrings
(a, b) => -1 | 0 | 1
Custom string comparison function
result
-1 | 0 | 1
Comparison result

Selection

select

Create a function that picks only selected fields from results.
import { select } from '@feathersjs/adapter-commons'

const selectFn = select(params, 'id')
const filtered = selectFn(data)
params
Params
required
Service parameters with query.$select
...otherFields
string[]
Additional fields to always include
result
(data: any) => any
Function that filters data to selected fields

Types

AdapterServiceOptions

Base options interface for all adapters.
interface AdapterServiceOptions {
  id?: string
  paginate?: PaginationParams
  multi?: boolean | string[]
  operators?: string[]
  filters?: FilterSettings
  events?: string[]
}

AdapterParams

Extended params interface with adapter-specific options.
interface AdapterParams<Q = AdapterQuery> extends Params<Q> {
  adapter?: Partial<AdapterServiceOptions>
  paginate?: PaginationParams
}

AdapterQuery

Query interface with standard filter parameters.
interface AdapterQuery extends Query {
  $limit?: number
  $skip?: number
  $select?: string[]
  $sort?: { [key: string]: 1 | -1 }
}

PaginationParams

Pagination configuration.
interface PaginationParams {
  default?: number  // Default page size
  max?: number      // Maximum allowed page size
}

FilterSettings

Custom filter converter functions.
type FilterSettings = {
  [key: string]: QueryFilter | true
}

type QueryFilter = (value: any, options: FilterQueryOptions) => any

InternalServiceMethods

Interface for hook-less internal methods (prefixed with _).
interface InternalServiceMethods<Result, Data, PatchData, Params> {
  _find(params?: Params): Promise<Paginated<Result> | Result[]>
  _get(id: Id, params?: Params): Promise<Result>
  _create(data: Data | Data[], params?: Params): Promise<Result | Result[]>
  _update(id: Id, data: Data, params?: Params): Promise<Result>
  _patch(id: Id | null, data: PatchData, params?: Params): Promise<Result | Result[]>
  _remove(id: Id | null, params?: Params): Promise<Result | Result[]>
}

VALIDATED Symbol

Symbol to mark queries as already validated.
import { VALIDATED } from '@feathersjs/adapter-commons'

// Mark query as validated to skip sanitization
const query = {
  status: 'active',
  [VALIDATED]: true
}

Example: Custom Adapter

import { AdapterBase, AdapterServiceOptions, AdapterParams } from '@feathersjs/adapter-commons'
import { Id, Paginated } from '@feathersjs/feathers'

interface MyAdapterOptions extends AdapterServiceOptions {
  connection: any
}

class MyCustomAdapter<Result, Data, ServiceParams extends AdapterParams, PatchData> extends AdapterBase<
  Result,
  Data,
  PatchData,
  ServiceParams,
  MyAdapterOptions
> {
  async _find(params?: ServiceParams): Promise<Paginated<Result> | Result[]> {
    const { query, filters } = this.filterQuery(null, params)
    const { paginate } = this.getOptions(params)
    
    // Your database query logic here
    const data = await this.runQuery(query, filters)
    
    if (paginate && paginate.default) {
      const total = await this.countRecords(query)
      return {
        total,
        data,
        limit: filters.$limit,
        skip: filters.$skip || 0
      }
    }
    
    return data
  }
  
  async _get(id: Id, params?: ServiceParams): Promise<Result> {
    // Implementation
  }
  
  async _create(data: Data | Data[], params?: ServiceParams): Promise<Result | Result[]> {
    if (Array.isArray(data)) {
      return Promise.all(data.map(item => this._create(item, params)))
    }
    // Implementation
  }
  
  async _update(id: Id, data: Data, params?: ServiceParams): Promise<Result> {
    // Implementation
  }
  
  async _patch(id: Id | null, data: PatchData, params?: ServiceParams): Promise<Result | Result[]> {
    if (id === null && !this.allowsMulti('patch', params)) {
      throw new MethodNotAllowed('Can not patch multiple entries')
    }
    // Implementation
  }
  
  async _remove(id: Id | null, params?: ServiceParams): Promise<Result | Result[]> {
    if (id === null && !this.allowsMulti('remove', params)) {
      throw new MethodNotAllowed('Can not remove multiple entries')
    }
    // Implementation
  }
}

// Create service
const service = new MyCustomAdapter({
  connection: myConnection,
  id: 'id',
  paginate: { default: 10, max: 50 }
})

Build docs developers (and LLMs) love