Knex.js Documentation

repository·main·Indexed 18 days ago

https://github.com/knex/documentation

Documentation for Knex.js, a SQL query builder for Node.js. Includes guides on installation, database driver support (PostgreSQL, MySQL, SQLite3, MSSQL, OracleDB), and extending builders. Features a collection of recipes for handling transactions, full-text indexing in PostgreSQL, Azure SQL Database connectivity, and SQLCipher encryption for SQLite.

Tokens
34.6K
Snippets
138
Records
149
Agent score
63%

What's inside Knex.js

  1. Use the Knex Query Builder to construct SQL queries

    main

    The Knex Query Builder is the primary interface for building and executing standard SQL queries, including select, insert, update, and delete. It provides a chainable API to construct complex queries programmatically.

    // Example of a basic query builder chain
    knex('users')
      .select('id', 'username')
      .where('id', 1);
  2. Alias identifiers in Knex

    main

    When passing table or column names to Knex methods, you can provide aliases using two different syntaxes:

    1. String Suffix: Use the as keyword within a string (e.g., 'columnName as aliasName').
    2. Object Mapping: Pass an object where the key is the desired alias and the value is the identifier (e.g., { aliasName: 'columnName' }).

    If an object contains multiple key-value pairs, Knex expands them into a comma-separated list of aliased identifiers.

    Important: Do not use identifier syntax to specify schemas (e.g., schemaName.tableName). This can lead to incorrect query rendering. Instead, use the .withSchema('schemaName') method.

    knex({ a: 'table', b: 'table' })
      .select({
        aTitle: 'a.title',
        bTitle: 'b.title'
      })
      .whereRaw('?? = ??', ['a.column_1', 'b.column_2'])
  3. Disable transactions for a specific migration

    main

    By default, Knex runs each migration inside a transaction. If you need to run a migration that cannot be wrapped in a transaction (e.g., certain DDL statements in some databases), you can disable it in two ways:

    1. Globally: Set disableTransactions: true in your knexfile migration configuration.
    2. Per-migration: Export a config object from the migration file itself with transaction: false.
    exports.up = function(knex) {
      return knex.schema.createTable('users', function (table) {
        table.increments('id');
      });
    };
    
    exports.down = function(knex) {
      return knex.schema.dropTable('users');
    };
    
    // Disable transaction for this specific file
    exports.config = { transaction: false };
  4. Bind parameters in raw SQL

    main

    Knex supports several ways to bind parameters in knex.raw() to ensure values are properly escaped:

    Positional Bindings

    • ?: Interpreted as a value.
    • ??: Interpreted as an identifier (e.g., table or column names).

    Named Bindings

    • :name: Interpreted as a value.
    • :name:: Interpreted as an identifier.

    Named bindings are processed as long as the provided value is not undefined.

    Single Bindings

    If you only have one binding, you can pass the value directly as the second argument to .raw() instead of an array.

    Array Bindings

    Knex does not have a unified syntax for array bindings (like IN (?)). You must manually generate the correct number of placeholders in your SQL string.

    // Positional: Identifier and Value
    knex('users').where(knex.raw('?? = ?', ['user.name', 1]))
    
    // Named: Identifier and Value
    const raw = ':name: = :thisGuy or :name: = :otherGuy or :name: = :undefinedBinding'
    knex('users').where(knex.raw(raw, {
      name: 'users.name',
      thisGuy: 'Bob',
      otherGuy: 'Jay',
      undefinedBinding: undefined
    }))
    
    // Single binding shortcut
    knex.raw('LOWER("login") = ?', 'knex')
    
    // Manual Array binding for IN clauses
    const myArray = [1, 2, 3]
    knex.raw('select * from users where id in (' + myArray.map(_ => '?').join(',') + ')', [...myArray]);
  5. Use transactionProvider for reusable transactions

    main

    A transactionProvider is a factory that creates a reusable transaction instance. The transaction does not actually start until the provider is called for the first time. Subsequent calls to the provider return the same transaction instance, allowing you to group multiple operations into a single unit of work across different parts of your code.

    // Does not start a transaction yet
    const trxProvider = knex.transactionProvider();
    
    const books = [
      {title: 'Canterbury Tales'},
      {title: 'Moby Dick'},
      {title: 'Hamlet'}
    ];
    
    // Starts a transaction
    const trx = await trxProvider();
    const ids = await trx('catalogues')
      .insert({name: 'Old Books'}, 'id')
    books.forEach((book) => book.catalogue_id = ids[0]);
    await trx('books').insert(books);
    
    // Reuses same transaction
    const sameTrx = await trxProvider();
    const ids2 = await sameTrx('catalogues')
      .insert({name: 'New Books'}, 'id')
    books.forEach((book) => book.catalogue_id = ids2[0]);
    await sameTrx('books').insert(books);
  6. Use dynamic connection configuration with expirationChecker

    main

    The connection parameter can be a function that returns a configuration object or a Promise. By default, this configuration is cached. To handle rotating credentials (like short-lived auth tokens), return an expirationChecker function within the configuration object. Knex will call this function; if it returns true, Knex will re-run the connection function to get a fresh configuration.

    const knex = require('knex')({
      client: 'postgres',
      connection: async () => {
        const { token, tokenExpiration } = await someCallToGetTheToken();
    
        return {
          host : 'your_host',
          port: 5432,
          user: 'your_database_user',
          password: token,
          database: 'myapp_test',
          expirationChecker: () => {
            return tokenExpiration <= Date.now();
          }
        };
      }
    });
  7. Augment the Tables interface for inferred types

    main

    To reduce boilerplate and enable automatic type inference across your application, you can augment the Tables interface in the 'knex/types/tables' module. This allows you to define your table structures once and have Knex automatically apply them to queries.

    For advanced use cases, you can use Knex.CompositeTableType to specify different interfaces for the base model (used for select, where, etc.), the 'insert' type (used for .insert()), and the 'update' type (used for .update()).

    import { Knex } from 'knex';
    
    declare module 'knex/types/tables' {
      interface User {
        id: number;
        name: string;
        created_at: string;
        updated_at: string;
      }
      
      interface Tables {
        // Basic typing: maps 'users' table to the User interface
        users: User;
    
        // Advanced typing: uses CompositeTableType for different operations
        users_composite: Knex.CompositeTableType<
          User, // Base type (select, where, etc.)
          Pick<User, 'name'> & Partial<Pick<User, 'created_at' | 'updated_at'>>, // Insert type
          Partial<Omit<User, 'id'>> // Update type
        >;
      }
    }
  8. Use Knex changelog functionality for seeds

    main

    To use Knex's changelog functionality (to ensure environments are only seeded once) without mixing seed files with migration files, you can pass an array of directories to the directory option in knex.migrate.latest. Set sortDirsSeparately: true to ensure Knex handles the different directory structures correctly.

    await knex.migrate.latest({
        directory: [
          'src/services/orders/database/migrations',
          'src/services/orders/database/seeds'
        ],
        sortDirsSeparately: true,
        tableName: 'orders_migrations',
        schemaName: 'orders',  
    })
  9. Extend Knex builders

    main

    You can add custom methods to Knex's internal builders using the .extend() method. This allows you to create reusable, domain-specific logic for schema manipulation, table definitions, view creation, or column configuration.

    Available builders to extend:

    • knex.SchemaBuilder: For schema-level operations.
    • knex.TableBuilder: For operations within a table definition.
    • knex.ViewBuilder: For view-specific operations.
    • knex.ColumnBuilder: For column-specific operations.

    Each extension function should typically return this to allow for method chaining.

    knex.SchemaBuilder.extend("functionName", function() {
        console.log('Custom Schema Builder Function');
        return this;
    });
    
    knex.TableBuilder.extend("functionName", function() {
        console.log('Custom Table Builder Function');
        return this;
    });
    
    knex.ViewBuilder.extend("functionName", function() {
        console.log('Custom View Builder Function');
        return this;
    });
    
    knex.ColumnBuilder.extend("functionName", function() {
        console.log('Custom Column Builder Function');
        return this;
    });
  10. Add TypeScript support for extended builders

    main

    When extending Knex builders in JavaScript, you must manually update your TypeScript declarations to ensure the new methods are recognized by the compiler. You can do this by using module augmentation on the knex module.

    Extend the Knex namespace by adding your new method signatures to the corresponding interfaces: SchemaBuilder, TableBuilder, ViewBuilder, or ColumnBuilder.

    import "knex";
    
    declare module "knex" {
        namespace Knex {
            interface SchemaBuilder {
                functionName (): Knex.SchemaBuilder;
            }
            interface TableBuilder {
                functionName (): Knex.TableBuilder;
            }
            interface ViewBuilder {
                functionName (): Knex.ViewBuilder;
            }
            interface ColumnBuilder {
                functionName (): Knex.ColumnBuilder;
            }
        }
    }