Objection.js — сравнительно молодая и минималистичная ORM-библиотека для Node.js, которая сильно упрощает взаимодействие с базами данных и не перегружена дополнительными функциями, как sequalize или typeORM. Разбираемся, в чем ее специфика и как строить запросы с ее помощью.
Это адаптированный перевод статьи A Step Towards Simplified Querying in NodeJS из блога компании Velotio Technologies. Повествование ведется от лица автора оригинала.
Недавно я увидел на StackOverflow историю разработчика, который столкнулся с проблемой использования ORM. Этот кейс натолкнул меня на мысль написать статью о альтернативе sequelize — Objection.js, библиотеке, которая решает множество проблем ORM.
Что такое ORM
ORM или метод реляционного сопоставления объектов — решениe для сопоставления объектов в программе с таблицами базы данных. Он позволяет разработчикам взаимодействовать с объектами вместо того, чтобы писать запросы к базе данных.
ORM позволяет строить динамические запросы с помощью Query Builder и абстрагироваться от особенностей конкретной базы данных. Решения обычно поддерживают Postgres, MySQL, SQLite и другие базы данных.
Вопрос на StackOverflow касался преобразования приведенного ниже запроса в sequelize-запрос. Стоит отметить, что его писал начинающий разработчик:
Решение на sequelize выглядит так:
Учитывая простоту запроса, решение кажется сложным, и чем больше в нем новых отношений, тем оно сложнее. Такие простые запросы в sequelize описаны недостаточно подробно.
Для сравнения разберем, как решение задачи будет выглядеть на Objection.js:
Буду честен — я сам пользуюсь Objection.js, что не делает его лучшим ORM для NodeJS. Каждая библиотека имеет свою специфику, а некоторые разработчики вообще выступают против любых ORM и предлагают вручную писать SQL-запросы. Отчасти они правы. Если ваше приложение достаточно маленькое, и вы можете написать несколько функций-хелперов для выполнения запросов, то используйте обычный SQL вместо ORM.
ORM нужна, когда в приложении много таблиц с большим количеством связей, которые нужно определить, и выполнить несколько объединенных запросов.
Список самых упоминаемых ORM для NodeJS выглядит так:
Sequelize
Objection.js
typeORM
Почему я выбрал Objection.js
В основе решения — мощный конструктор SQL-запросов KNEX.JS.
Библиотека позволяет выполнять запросы через функции async/await.
Позволяет добавить валидацию с помощью JSON схемы.
Позволяет создавать модели для таблиц с использованием синтаксиса ES6/ES7 и определять связи между ними.
Objection.js автоматически строит связи между объектами и самостоятельно определяет, какие данные нужно сохранить, а какие — обновить (за это отвечают функции
upsert
иinsert
).
У Objection.js и KNEX.JS очень понятная и обширная документация, в которой легко найти инструкции и примеры использования. В официальном репозитории библиотеки на GitHub есть множество примеров использования библиотеки и подробная инструкция по ее установке и настройке.
Создание и управление схемой базы данных
Миграция — один из подходов к внесению изменений в базу данных, в этой статье сосредоточимся на нем. В Objection.js используются механизмы миграции KNEX.JS. Поскольку речь идет об ORM, определять таблицы и столбцы можно не с помощью SQL, а непосредственно написанием кода на JavaScript. Для создания новой миграции выполним команду KNEX CLI:
После выполнения этой команды в каталоге миграций появится новый файл с текущей временной меткой и именем, которое мы присвоили этой миграции. Выглядит он следующим образом:
Функция exports.up
определяет команды, выполнение которых внесет нужны изменения в базу данных. Например, речь может идти о создании таблиц базы данных, добавлении или удалении столбца из таблицы, изменении индексов и других действиях.
Вторая функция, exports.down
, выполняет противоположные первой действия: например, когда exports.up
создает таблицу, exports.down удаляет ее. Она нужна для того, чтобы быстро откатить изменения, сделанные миграцией, если это потребуется.
В случае ниже в функции up
создается таблица persons
с необходимыми столбцами и индексами, а в функции down
таблица удаляется, если она существует:
Запустить процесс миграции можно следующим образом:
Также можно передать флаг — env
или установить NODE_ENV
, чтобы выбрать альтернативную среду:
Модели
Модели базы данных — абстракции вокруг таблиц базы данных, которые помогают инкапсулировать бизнес-логику внутри них. В Objection.js модели можно создавать c помощью классов ES.
Прежде чем продолжить, уточним, что модели в Objection.js не создают таблиц в базах данных, а предназначены для добавления валидаций и создания связей между моделями. Например:
В этом коде есть три статистических геттера. Первый, tableName
, возвращает имя таблицы.
Второй, jsonSchema
, определяет параметры валидации каждого поля. В нем можно указать необходимые свойства: например, тип поля (число, строку, объект и тд).
Третий, relationMappings
, определяет связь выбранной модели с другими моделями. В данном случае ключ внешнего объекта pets
— ссылка на связанный класс. Свойство join
определяет, как модели связаны друг с другом, а from
и to
объекта join
определяют поля базы данных, с помощью которых связаны модели. Класс ModelClass является классом связанной модели.
Таким образом, класс Person
имеет связь HasManyRelation
(один ко многим) с моделью Animal
. Модели связаны через столбцы persons.id
и animals.OwneId
. В результате мы видим, что у одного человека может быть несколько домашних животных.
Запросы
Начнем с простого запроса SELECT:
Простой SELECT будет выглядеть на Objection.js следующим образом.:
Запрос выше затрагивал все поля в таблице, а так можно реализовать запрос с выбором одного поля:
Из примеров выше видно, насколько запросы в Objection.js похожи на SQL-запросы. Преобразовать одни запросы в другие довольно просто, что нетипично для ORM.
Запросы создания и вставки
Другой способ его выполнения выглядит так:
Запросы удаления
Другой способ его выполнения выглядит так:
Запросы по отношениям
Предположим, мы хотим получить данные обо всех домашних животных пользователя по имени Бен:
Теперь предположим, что вы хотите добавить информацию о пользователе со всеми его животными. Для этого можно использовать запрос с помощью графа:
Доступные плагины
objection-password — плагин, который позволяет автоматически хэшировать пароли для модели Objection.js. Это легкий способ защитить пароли и другие конфиденциальные данные.
objection-graphql — автоматически генерирует GraphQL API для модели Objection.js.
Заключение
В заключение стоит отметить, что если передо мной встанет выбор, какую ORM использовать, я выберу Objection.js. В первую очередь потому, что запросы в ней выполняются проще, чем в sequalize.
В целом Objection.js не похоже на другие ORM. По сути, это просто оболочка поверх конструктора запросов KNEX.js, поэтому лучше всего воспринимать решение как конструктор запросов с дополнительными функциями.