ORM для баз данных SQL с Node.js

Каждый, кто когда-либо создавал приложение стека MEAN или MERN, вероятно, знаком с Mongoose, полезной библиотекой моделирования объектных данных, которая значительно упрощает работу с MongoDB. А поскольку этот стек приложений настолько популярен, существует около тысячи руководств (одни намного лучше других), демонстрирующих, как использовать MongoDB, Express, React (или Angular) и Node.js вместе.

Каково же было мое удивление, когда я решил создать простое приложение с полным стеком с использованием MySQL (или SQLite, или PostgreSQL, или любого другого типа базы данных SQL) и не нашел почти ничего, что имело бы смысл в том, как это сделать с помощью Express и Node. Действительно? Ничего недавнего? Ничего подобного с продуманной модульной архитектурой? Похоже, ни у кого не было подходящего способа сделать это - ничего, кроме реальных SQL-запросов, записанных в файле server.js для выполнения операций CRUD с базой данных.

Затем я нашел Sequelize. И все изменилось. Я хотел поделиться тем, что я здесь узнал, в надежде спасти некоторых других людей от массы устаревших руководств, не подозревающих, насколько проще Sequelize может упростить внедрение базы данных SQL в свои приложения Node.

Что такое Sequelize?

Домашняя страница Sequelize лучше всего описывает, что это такое:

Sequelize - это ORM на основе обещаний для Node.js v4 и выше. Он поддерживает диалекты PostgreSQL, MySQL, SQLite и MSSQL и обеспечивает надежную поддержку транзакций, отношений, репликации чтения и многого другого. - Продолжить

Это самый простой и понятный инструмент, который я нашел для работы с базами данных на SQL внутри приложения JavaScript. Есть и другие варианты, предлагающие аналогичные функции. На ум приходит Knex.js, но после сравнения их статистики на Github я решил продолжить работу с Sequelize.

Вот некоторые статистические данные, которые помогли мне принять решение:

  • У Sequelize более 8000 коммитов (у Knex чуть меньше 2000),
  • Имеет 375 релизов (у Knex 119),
  • 694 участника (у Knex 237),
  • Недавно была выпущена бета-версия 5 (Knex находится на v0.15),
  • И у него есть интеграция с CLI, TypeScript и GraphQL (Knex не имеет),
  • Плюс 883 000 загрузок в месяц из NPM (у Knex чуть меньше 600 000 в месяц).

Практически во всех отношениях (по цифрам) Sequelize превосходит Knex, поэтому я решил использовать то, что кажется очень стабильной, хорошо документированной, долго поддерживаемой и часто обновляемой библиотекой.

Я скажу, что Knex предлагает встроенную поддержку Oracle и Amazon Redshift, но, поскольку я не собирался использовать базу данных Oracle, это не было для меня основным коммерческим аргументом.

Итак, когда вы используете Sequelize, вы получаете множество тех же преимуществ, которые Mongoose предлагает пользователям MongoDB, плюс некоторые бонусы:

  • Возможность подключаться с очень небольшими изменениями кода к нескольким различным типам баз данных SQL,
  • Объектные модели (похожие на схемы Mongoose), которые типизированы, сопоставлены с таблицами и могут потребоваться,
  • Базовые и более сложные параметры запросов, такие как findAll/findOne, destroy, update и т. Д., А также такие вещи, как разбиение на страницы, вложенные объекты и ассоциации,
  • Пул соединений,
  • Сырые запросы,
  • И многое другое у меня нет времени рассказать.

Документация очень тщательная и показывает, сколько может предложить Sequelize по сравнению с необходимостью управлять всем этим самостоятельно.

Я также хотел бы отметить, что с помощью Sequelize вы можете избавить себя от необходимости переписывать SQL-запросы, чтобы они соответствовали небольшим вариациям в диалектах базы данных - ваши find и create операторы не нуждаются в модификациях для работы независимо от того, какой тип БД вы используете. повторное подключение к. Не знаю, как вы, но я большой поклонник меньших усилий с моей стороны.

Sequelize также использует обещания через Bluebird для управления своим асинхронным потоком управления. Все это означает, что когда вы запрашиваете базу данных через Sequelize, она вернет обещание, подобное этому.

User.findOne().then(user => {   
    console.log(user.get('firstName')); 
});
// returns user object's first name, like 'John'

Это также означает, что Sequelize хорошо работает с такими функциями ES6 async / await.

user = await User.findOne()  
console.log(user.get('firstName'));
// also returns user object's first name, like 'John'

Как реализовать Sequelize в Express

Отлично, теперь вы можете увидеть некоторые причины для использования Sequelize, как вы используете его в своем проекте?

Это всегда мой самый большой вопрос, когда я вижу образцы или учебные пособия. Как мне его использовать? И как мне организовать свои файлы и структуру моей программы, чтобы можно было легко менять функциональные возможности с минимальными изменениями с моей стороны?

Вот структура исходного файла, которую я придумал (включая только серверные (API) папки). Это довольно просто, потому что проект, над которым я работал, не был особенно сложным. Но он предлагает достаточную организацию, чтобы упростить добавление функций CRUD через папку routes, а также определение одной или нескольких моделей баз данных благодаря папке models.

root/
├── api/ 
| ├── server.js 
| ├── sequelize.js 
| ├── package.json
| ├── models/ 
| | ├── user.js
| ├── routes/ 
| | ├── deleteUser.js 
| | ├── findUser.js 
| | ├── loginUser.js 
| | ├── registerUser.js
| | ├── updateUser.js
| ├── data/ (optional depending on storage choice - like SQLite) 
| | ├── db/ 
| | | ├── storage.sqlite
| ├── node-modules/

Итак, теперь, когда вы видите файловую структуру, позвольте мне показать вам, что будут содержать ваши файлы. Я начну с файла user.js, потому что он импортируется в файлы sequlize.js и routes.

Определите модель (ы)

Поскольку я создавал приложение для регистрации пользователей, мой пользовательский файл был очень простым. Просто идентификатор, который был установлен в качестве первичного ключа и будет автоматически увеличиваться, имя, фамилия, адрес электронной почты, имя пользователя и пароль (оба обязательны). За этим довольно легко следить.

Настройка подключения к базе данных

Далее идет файл sequelize.js, здесь я сохранил большую часть настроек моей базы данных, чтобы упростить переключение баз данных, если вы когда-либо хотели перейти, скажем, с базы данных MySQL на базу данных SQLite. Вот как выглядело соединение MySQL:

Как видите, в строке 4 я инициирую соединение с базой данных MySQL, давая ей имя базы данных, имя пользователя, пароль, а затем указываю хост и диалект. Вот и все.

Затем я добавил пользовательскую модель, определенную ранее как константу в строке 9, и функция sequelize.sync() синхронизирует все определенные модели с базой данных. Таким образом, он создает базу данных и таблицы на основе предоставленной вами информации и моделей, которые вы ей передали.

Наконец, я экспортирую константу User, которую я определил ранее, для использования в других частях проекта, таких как маршруты.

Вот еще один пример подключения к SQLite, чтобы вы могли увидеть различия:

Помимо небольших изменений в настройке базы данных, все остальное в файле остается неизменным.

Определите маршруты

Хорошо, я почти закончил с настройкой. Чтобы сохранить модульность кода (и упростить для меня добавление или вычитание функциональности из приложения), я добавил каждую часть функциональности CRUD в базу данных в виде отдельного файла. Вы можете добавить их все в один и тот же файл и разделить его по моделям или как угодно, что вам проще всего организовать.

Вот пример моего маршрута регистрации пользователя (функция создания). Я добавил хеширование паролей с помощью bcrypt, но сейчас это не важно.

Как видно из приведенного выше снимка экрана, модель User импортируется из файла sequelize.js, данные, вводимые пользователем, передаются с клиентской стороны приложения через поля req.body.xyz, а затем я выполняю несколько проверок в базе данных с помощью помощь Sequelize перед добавлением нового пользователя.

Первая проверка выполняется в строке 14, когда я проверяю, что пользователь предоставил и имя пользователя, и пароль (в соответствии с требованиями модели), если их там нет, сервер отправляет обратно сообщение об ошибке 'username and password required. После этого проверяю, я запрашиваю базу данных, чтобы узнать, занято ли это имя пользователя, если это так, сервер снова отправляет 'username is already taken обратно клиенту. Если он проходит эту проверку, пароль хешируется, а информация о пользователе сохраняется в базе данных. После успешного сохранения он отправляет клиенту 'user created' сообщение об успешном завершении.

И вся операция создания заключена в оболочку, поэтому в случае возникновения проблем со связью с сервером (он не работает, есть проблемы с сетью и т. Д.), Он все равно может отправить сообщение клиенту, сообщая ему, что возникла проблема (строки 44–47 ).

Добавить маршруты на сервер

Теперь я готов добавить эти маршруты на сервер. Это самая легкая часть.

Все, что вам нужно сделать, чтобы эта работа заработала, - это потребовать маршруты в вашем server.js файле, как я сделал в строках 14–18, и заставить их принять ваше приложение Express, и все готово. Нет ничего проще.

Теперь вы должны иметь возможность использовать Sequelize сколько душе угодно с любой базой данных SQL в вашем проекте Node.js, Express.

Если вы хотите увидеть полный пример стека MERN с использованием Sequelize, вы можете увидеть мое репо здесь. Из основной ветки вы можете запустить docker-compose build, а затем docker-compose up, а из ветки sqlite вы можете запускать файлы клиента и сервера по отдельности с помощью npm start из папок api и client.

Заключение

Sequelize привносит порядок и гибкость в использование баз данных SQL в приложениях JavaScript. Это снижает потребность в написании необработанных запросов, управлении подключениями, защите от вредоносных атак SQL и многом другом. А с некоторой довольно простой настройкой вы тоже можете использовать Sequelize в своих собственных JS-проектах - и, надеюсь, ускорить время разработки. Я обязательно продолжу использовать этот инструмент в будущем.

Спасибо за чтение, надеюсь, это окажется полезным. Хлопки очень ценятся!

Если вам понравилось это читать, возможно, вам понравятся и другие мои блоги:

Ссылки и дополнительные ресурсы: