Knex.js для JavaScript (Node.js): полное руководство с примерами

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Knex.js для JavaScript (Node.js): полное руководство с примерами

1. Что это и зачем нужна

Knex.js — это мощный, гибкий и производительный SQL query builder для Node.js. В отличие от ORM (Object-Relational Mapping) библиотек, таких как Sequelize или TypeORM, Knex.js не пытается полностью абстрагировать вас от SQL. Вместо этого он предоставляет удобный, цепной (chainable) API для программного построения SQL-запросов, сохраняя при этом полный контроль над результирующим SQL. Это делает Knex.js идеальным выбором для проектов, где важна производительность, прозрачность запросов и гибкость, но при этом хочется избежать написания сырых строк SQL с риском синтаксических ошибок и SQL-инъекций.

Основная задача Knex.js — упростить взаимодействие с реляционными базами данных (PostgreSQL, MySQL, SQLite3, MSSQL, Oracle) из JavaScript. Он берет на себя такие рутинные задачи, как экранирование идентификаторов и значений (защита от SQL-инъекций), управление пулом соединений, миграции схемы данных (создание, изменение таблиц) и "посев" (seeding) тестовых данных. Knex.js также поддерживает транзакции, подзапросы, оконные функции, raw-запросы и многое другое, что делает его универсальным инструментом как для небольших проектов, так и для высоконагруженных систем.

Для образовательного сайта PythonLib, который фокусируется на практическом обучении, Knex.js представляет особый интерес. Он позволяет студентам, уже знакомым с основами SQL, быстро перейти к написанию безопасного и эффективного кода на Node.js. Изучение Knex.js — это отличный мост между теорией SQL и реальной backend-разработкой. Вы не просто учите ORM-магию, а понимаете, как каждый ваш метод транслируется в конкретный SQL-запрос, что критически важно для отладки и оптимизации.

2. Установка

Установка Knex.js производится через менеджер пакетов npm. Вам также потребуется установить драйвер для вашей базы данных.

# Установка Knex.js
npm install knex --save

# Установка драйвера для PostgreSQL
npm install pg --save

# Установка драйвера для MySQL / MariaDB
npm install mysql2 --save

# Установка драйвера для SQLite3
npm install sqlite3 --save

# Установка драйвера для MSSQL
npm install tedious --save

# Установка драйвера для Oracle
npm install oracledb --save

Важно: Драйвер базы данных устанавливается отдельно. Knex.js использует его для непосредственного взаимодействия с БД. После установки вы можете глобально установить Knex CLI для управления миграциями и сидами: npm install knex -g.

3. Быстрый старт — минимальный рабочий пример

Создайте файл app.js и подключитесь к базе данных SQLite (не требует установки сервера).

// Подключаем Knex
const knex = require('knex');

// Конфигурация подключения к SQLite
const db = knex({
    client: 'sqlite3', // Тип базы данных
    connection: {
        filename: './mydb.sqlite' // Путь к файлу базы данных
    },
    useNullAsDefault: true // Рекомендуется для SQLite
});

// Асинхронная функция для демонстрации
async function main() {
    // 1. Создаем таблицу 'users', если её нет
    await db.schema.createTableIfNotExists('users', (table) => {
        table.increments('id'); // Автоинкрементный первичный ключ
        table.string('name');   // Строковое поле
        table.integer('age');   // Целочисленное поле
    });

    // 2. Вставляем данные
    await db('users').insert({ name: 'Alice', age: 30 });
    await db('users').insert({ name: 'Bob', age: 25 });

    // 3. Выбираем все записи
    const users = await db.select('*').from('users');
    console.log('Все пользователи:', users);

    // 4. Выбираем с условием
    const youngUsers = await db('users').where('age', '<', 28);
    console.log('Молодые пользователи:', youngUsers);

    // 5. Закрываем соединение
    await db.destroy();
}

main().catch(console.error);

Запустите скрипт: node app.js. Вы увидите в консоли результаты запросов. Файл mydb.sqlite будет создан автоматически. Этот пример показывает базовый цикл: создание схемы, вставка, выборка и фильтрация.

4. Основные методы/функции/классы

Рассмотрим ключевые методы Knex.js. Все запросы возвращают Promise, поэтому они должны обрабатываться с помощью await или .then().

4.1. knex(config) — Инициализация экземпляра

Сигнатура: knex(config: object): Knex

Что делает: Создает и возвращает экземпляр Knex, настроенный для работы с конкретной СУБД. Это основной класс, от которого строятся все запросы.

// Пример для PostgreSQL
const db = require('knex')({
    client: 'pg',
    connection: {
        host: 'localhost',
        port: 5432,
        user: 'myuser',
        password: 'mypassword',
        database: 'mydb'
    },
    pool: { min: 2, max: 10 } // Настройка пула соединений
});

4.2. .select([columns]) — Выборка данных

Сигнатура: knex.select([column1, column2, ...]): QueryBuilder

Что делает: Инициирует SELECT запрос. Если столбцы не указаны, выбираются все (*).

// Выбрать имена и возраст всех пользователей
const result = await db.select('name', 'age').from('users');
console.log(result); // [{name: 'Alice', age: 30}, ...]

// Выбрать все столбцы
const all = await db.select().from('users');

4.3. .from(tableName) — Указание таблицы

Сигнатура: knex.from(tableName: string): QueryBuilder

Что делает: Указывает таблицу, из которой производится выборка или удаление. Может использоваться как синоним .table().

// Эквивалентные запросы
const users1 = await db.from('users');
const users2 = await db('users'); // Сокращенная форма

4.4. .where(column, operator, value) — Фильтрация

Сигнатура: knex.where(column: string, operator?: string, value: any): QueryBuilder

Что делает: Добавляет условие WHERE к запросу. Поддерживает различные операторы (=, <, >, IN, LIKE и т.д.).

// Простое условие (оператор '=' по умолчанию)
const alice = await db('users').where('name', 'Alice');

// Условие с оператором
const adults = await db('users').where('age', '>=', 18);

// Множественные условия (AND)
const specific = await db('users').where('name', 'Bob').andWhere('age', 25);

// Условие IN
const names = await db('users').whereIn('name', ['Alice', 'Bob']);

4.5. .insert(data) — Вставка данных

Сигнатура: knex.insert(data: object | array): QueryBuilder

Что делает: Вставляет одну или несколько записей в таблицу. Возвращает массив с ID вставленных записей (зависит от драйвера).

// Вставка одной записи
const [newUserId] = await db('users').insert({ name: 'Charlie', age: 35 });
console.log('ID нового пользователя:', newUserId);

// Вставка нескольких записей
await db('users').insert([
    { name: 'David', age: 28 },
    { name: 'Eve', age: 22 }
]);

4.6. .update(data) — Обновление данных

Сигнатура: knex.update(data: object): QueryBuilder

Что делает: Обновляет записи, соответствующие условию where. Возвращает количество обновленных строк.

// Обновить возраст пользователя с именем 'Alice'
const updatedCount = await db('users')
    .where('name', 'Alice')
    .update({ age: 31 });
console.log(`Обновлено строк: ${updatedCount}`);

4.7. .del() — Удаление данных

Сигнатура: knex.del(): QueryBuilder

Что делает: Удаляет записи, соответствующие условию. Возвращает количество удаленных строк. Будьте осторожны: вызов .del() без .where() удалит все записи в таблице!

// Удалить пользователя с ID = 3
const deletedCount = await db('users').where('id', 3).del();
console.log(`Удалено строк: ${deletedCount}`);

4.8. .join(table, firstCol, operator, secondCol) — Объединение таблиц

Сигнатура: knex.join(table: string, firstCol: string, operator?: string, secondCol: string): QueryBuilder

Что делает: Выполняет SQL JOIN (по умолчанию INNER JOIN) для объединения данных из двух таблиц.

// Предположим, есть таблица 'orders' с полем 'user_id'
const ordersWithUsers = await db('orders')
    .join('users', 'orders.user_id', '=', 'users.id')
    .select('orders.id as order_id', 'users.name', 'orders.total');
console.log(ordersWithUsers);

4.9. .orderBy(column, direction) — Сортировка

Сигнатура: knex.orderBy(column: string, direction?: 'asc' | 'desc'): QueryBuilder

Что делает: Добавляет сортировку ORDER BY к запросу.

// Сортировка по возрасту по убыванию
const sortedUsers = await db('users').orderBy('age', 'desc');

// Сортировка по нескольким полям
const sortedUsers2 = await db('users').orderBy([
    { column: 'age', order: 'desc' },
    { column: 'name', order: 'asc' }
]);

4.10. .limit(value) и .offset(value) — Пагинация

Сигнатура: knex.limit(value: number): QueryBuilder и knex.offset(value: number): QueryBuilder

Что делает: Ограничивает количество возвращаемых строк (LIMIT) и пропускает указанное количество строк (OFFSET). Используется для пагинации.

// Пропустить 2 записи и взять следующие 3
const page = await db('users')
    .offset(2)
    .limit(3)
    .orderBy('id', 'asc');

5. Реальный практический пример (100-200 строк кода)

Создадим простой REST API для управления списком задач (Todo List) с использованием Express.js и Knex.js. Пример демонстрирует CRUD-операции и транзакции.

const express = require('express');
const knex = require('knex');

// 1. Конфигурация Knex (SQLite для простоты)
const db = knex({
    client: 'sqlite3',
    connection: { filename: './todos.db' },
    useNullAsDefault: true
});

const app = express();
app.use(express.json());

// 2. Middleware для инициализации таблицы
app.use(async (req, res, next) => {
    await db.schema.createTableIfNotExists('todos', (table) => {
        table.increments('id');
        table.string('title').notNullable();
        table.boolean('completed').defaultTo(false);
        table.timestamp('created_at').defaultTo(db.fn.now());
    });
    next();
});

// 3. CRUD маршруты

// GET /todos — Получить все задачи
app.get('/todos', async (req, res) => {
    try {
        const todos = await db.select('*').from('todos').orderBy('created_at', 'desc');
        res.json(todos);
    } catch (error) {
        res.status(500).json({ error: error.message });
    }
});

// POST /todos — Создать новую задачу
app.post('/todos', async (req, res) => {
    const { title } = req.body;
    if (!title) {
        return res.status(400).json({ error: 'Title is required' });
    }
    try {
        const [id] = await db('todos').insert({ title });
        const newTodo = await db('todos').where({ id }).first();
        res.status(201).json(newTodo);
    } catch (error) {
        res.status(500                    

Рекомендаций