Skip to main content

Overview

The KyselyPlugin interface is the foundation for all Kysely plugins. Plugins allow you to intercept and modify queries before execution and transform results after execution.

Interface Definition

interface KyselyPlugin {
  transformQuery(args: PluginTransformQueryArgs): RootOperationNode
  transformResult(args: PluginTransformResultArgs): Promise<QueryResult<UnknownRow>>
}

Methods

transformQuery

transformQuery(args: PluginTransformQueryArgs): RootOperationNode
This method is called for each query before it is executed. You can modify the query by transforming its OperationNode tree. Parameters:
  • args.queryId - Unique identifier for the query
  • args.node - The root operation node representing the query
Returns: The transformed root operation node Usage: You typically use an OperationNodeTransformer to transform the query tree:
import { OperationNodeTransformer, KyselyPlugin } from 'kysely'

class MyTransformer extends OperationNodeTransformer {
  // Override specific transformation methods
}

class MyPlugin implements KyselyPlugin {
  #transformer = new MyTransformer()

  transformQuery(args: PluginTransformQueryArgs): RootOperationNode {
    return this.#transformer.transformNode(args.node, args.queryId)
  }
}

transformResult

transformResult(args: PluginTransformResultArgs): Promise<QueryResult<UnknownRow>>
This method is called for each query after it has been executed. You can modify the result and return the modified result. Parameters:
  • args.queryId - Unique identifier for the query (same as in transformQuery)
  • args.result - The query result containing rows and metadata
Returns: A promise resolving to the transformed query result Example:
async transformResult(
  args: PluginTransformResultArgs
): Promise<QueryResult<UnknownRow>> {
  if (args.result.rows && Array.isArray(args.result.rows)) {
    return {
      ...args.result,
      rows: args.result.rows.map(row => transformRow(row))
    }
  }
  
  return args.result
}

Sharing Data Between Methods

If you need to pass query-related data between transformQuery and transformResult, use a WeakMap with args.queryId as the key:
import type {
  KyselyPlugin,
  QueryResult,
  RootOperationNode,
  UnknownRow
} from 'kysely'

interface MyData {
  // Your data structure
}

const data = new WeakMap<any, MyData>()

const plugin = {
  transformQuery(args: PluginTransformQueryArgs): RootOperationNode {
    const something: MyData = {
      // Collect data during query transformation
    }

    data.set(args.queryId, something)
    return args.node
  },

  async transformResult(
    args: PluginTransformResultArgs
  ): Promise<QueryResult<UnknownRow>> {
    const something = data.get(args.queryId)
    // Use the data to transform results
    return args.result
  }
} satisfies KyselyPlugin
Use a WeakMap instead of a Map because transformQuery is not always matched by a call to transformResult, which would leave orphaned items in the map and cause a memory leak.

Using Plugins

Global Installation

Add plugins when creating a Kysely instance:
import { Kysely } from 'kysely'

const db = new Kysely<Database>({
  dialect,
  plugins: [new MyPlugin()]
})

Per-Query Installation

Apply a plugin to a single query:
const result = await db
  .selectFrom('person')
  .selectAll()
  .withPlugin(new MyPlugin())
  .execute()

Built-in Plugins

Kysely provides several built-in plugins:

See Also

Build docs developers (and LLMs) love