В официальной документации NestJS есть много примеров, показывающих, как использовать ORM-фреймворк, такой как TypeORM, для интеграции с базой данных. Однако что, если вы не хотите использовать ORM, а предпочитаете выполнять необработанные запросы к базе данных без накладных расходов ORM?

В этом руководстве вы найдете пример того, как это можно сделать. В этом руководстве мы напишем модуль, который будет отвечать за выполнение SQL-запросов в базе данных PostgreSQL.

Обязательные зависимости

Чтобы это работало, нам нужно установить библиотеку, которая позволит нам взаимодействовать с базой данных PostgreSQL. Node-postgres - де-факто стандартная библиотека для этого, так что давайте установим ее:

npm install pg

Поскольку NestJS использует TypeScript, мы также можем установить определения TypeScript для node-postgres:

npm install @types/pg --save-dev

Конфигурационный файл

Конфигурация базы данных обычно хранится во внешнем файле конфигурации, и у вас часто будут разные файлы для разных сред. Для целей этого руководства мы сохраним настройки нашей базы данных в простом .env файле:

Чтение этого файла не должно быть проблемой, если мы будем использовать встроенный ConfigModule от NestJS. Мы импортируем этот модуль в наш AppModule и сделаем его глобальным:

Делать модули глобальными нечасто, если вы действительно не планируете использовать их в большинстве своих прикладных модулей. Чтение конфигурации - обычная задача, поэтому имеет смысл сделать ConfigModule глобальным. Таким образом, нам не нужно явно импортировать его в каждый модуль приложения. Другое свойство конфигурации, envFilePath, - это путь к нашему .env файлу.

Настроив и импортировав ConfigModule, теперь мы можем использовать ConfigService во всем приложении для извлечения значений из нашего файла конфигурации. Например, в нашем случае configService.get('POSTGRES_USER') вернет my-user.

Модуль базы данных

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

Цель этого модуля - подключиться к базе данных и предоставить службу для выполнения запросов к базе данных.

Рекомендуемый способ подключения к базе данных из приложения NestJS - через пулы баз данных. Пул базы данных - это просто объект node-postgres, который отвечает за управление подключениями к базе данных PostgreSQL.

Поскольку мы будем внедрять этот пул в нашу службу базы данных, мы можем создать его, используя фабричный поставщик. Фабричные поставщики позволяют нам использовать внедрение зависимостей для внедрения других поставщиков, таких как ConfigService.

В приведенном выше фрагменте мы определили нашего поставщика в массиве providers модуля, назвали его DATABASE_POOL и сказали NestJS внедрить экземпляр ConfigService в нашу фабрику, чтобы мы могли инициализировать объект пула с конфигурацией из нашего .env файла.

Теперь давайте создадим службу базы данных, которая будет использоваться для выполнения запросов. Метод executeQuery пока пуст, и мы внедряем объект нашего пула с именем DATABASE_POOL в службу. Мы также создали экземпляр регистратора NestJS, чтобы мы могли регистрировать запросы к базе данных по мере их возникновения.

Теперь нам нужно экспортировать этот DatabaseService из нашего DatabaseModule, чтобы другие модули приложения могли его использовать:

Теперь мы можем закончить executeQuerymethod, который будет вызывать pool.query() для выполнения запросов к базе данных:

Метод прост: он принимает текст запроса, необязательные параметры и возвращает результирующий набор строк.

Наконец: очистка

Пул базы данных, который мы инициализировали в нашем DatabaseModule, не будет устанавливать никаких подключений, пока не будет выполнен первый запрос. Когда это происходит, создается новый клиент подключения, который сохраняется в пуле, пока пул жив. Если приложение закрывается, этот пул внезапно закрывается без какой-либо очистки.

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

Включив перехватчики выключения, мы можем записать реальный перехватчик выключения в нашем DatabaseModule.

Мы используем ссылку на модуль, чтобы получить экземпляр объекта нашего пула из контекста модуля, а затем вызываем его метод end() для отключения всех активных клиентов. Поскольку этот метод возвращает обещание, NestJS будет ждать в последовательности завершения работы, пока обещание не будет разрешено или отклонено.

Заключение

Спасибо за прочтение. Надеюсь, вы нашли этот урок полезным. Непосредственное взаимодействие с базой данных может быть не таким распространенным вариантом использования в NestJS, но иногда это может быть полезно, когда вам нужен больший контроль над выполняемыми запросами. Если у вас возникнут проблемы, дайте мне знать в комментариях.