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