npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@application-kit/typeorm

v0.5.0

Published

Курсорный листинг на TypeORM: контракты листинга, пагинатор, фильтры, ORM-база сущности, конфиг и корневой модуль соединения Postgres, SQL-логгер с маской bind-параметров

Readme

@application-kit/typeorm

Курсорный листинг на TypeORM: контракты листинга (пагинатор, сортировка, фильтры), курсорный пагинатор поверх SelectQueryBuilder, перевод фильтров в where, ORM-база сущности и конфиг соединения с Postgres из окружения.

Установка

yarn add @application-kit/typeorm

Peer-зависимости: typeorm, @nestjs/common; для входа /nest — @nestjs/cqrs, @nestjs/typeorm, nestjs-pino, class-transformer, class-validator, reflect-metadata; для DatabaseLogger входа /logging — nestjs-pino (объявлены опциональными: корню и /listing они не нужны).

Точки входа

| Вход | Что внутри | Зависимости | |---|---|---| | @application-kit/typeorm/listing | FilterInterface, FiltersMatchMode, SortingInterface, SortOrder, PaginatorInterface, ListingRepositoryInterface | нет — годится для браузерного кода | | @application-kit/typeorm | getListByCursor, CursorPaginator, getWhereByFilters, isUniqueViolation, estimateRowCount, withIdTiebreaker, AbstractOrmEntity, PackageOrmInterface | typeorm, @nestjs/common | | @application-kit/typeorm/nest | TypeormRootModule, ListingQuery, ListingHandler, DatabaseConfig, DatabaseEnvironment, createCliDataSource | @nestjs/common, @nestjs/cqrs, @nestjs/typeorm, nestjs-pino, class-transformer, class-validator, reflect-metadata | | @application-kit/typeorm/logging | createTypeormLogger, TypeormQueryLogger, createSqlParameterMask, runAsInfrastructureQuery, isInfrastructureQuery, SqlLoggerInterface, DatabaseLogger | nestjs-pino — createTypeormLogger и DatabaseLogger |

Клиент (Vue, GraphQL-типы) импортирует только /listing: корень тянет TypeORM и Nest. Nest-интеграции (DI-модули, CQRS, конфиг) — только во входе /nest, корень их не реэкспортирует и @nestjs/typeorm/nestjs-pino не тянет.

Листинг в репозитории

import { getListByCursor } from '@application-kit/typeorm'
import type { ListingRepositoryInterface } from '@application-kit/typeorm/listing'

export class UserRepository implements ListingRepositoryInterface<User> {
  public async list(paginator, sorting, filters) {
    return getListByCursor(UserEntity, this.repository.createQueryBuilder('user'), toDomain, paginator, filters, sorting)
  }
}

Неверный курсор страницы — InvalidPageCursorError (BadRequestException, HTTP 400).

Обработчик query

import { ListingHandler, ListingQuery } from '@application-kit/typeorm/nest'

export class GetUsersQuery extends ListingQuery {}

@QueryHandler(GetUsersQuery)
export class GetUsersHandler extends ListingHandler<User> {
  public constructor(@Inject(USER_REPOSITORY) repository: UserRepository) {
    super(repository)
  }
}

ORM-база

AbstractOrmEntity — колонки id (uuid), createdAt, updatedAt; OrmEntityProps<E> — свойства сущности без них. Имя отличается от доменного AbstractEntity из DDD-библиотек намеренно.

Логгер SQL

TypeormQueryLogger — адаптер Logger TypeORM поверх любого pino-совместимого логгера (SqlLoggerInterface: debug/info/warn/error). SQL приложения пишется на info с bind-параметрами, медленный запрос — warn, упавший — error с ошибкой под ключом err; SQL из области runAsInfrastructureQuery (пробы health, которые иначе дают до 97 % потока логов сервиса) уходит на debug.

createTypeormLogger(pinoParams, maskSqlParameters?) — сборка на pino-логгере: TypeormQueryLogger поверх DatabaseLogger с маской по умолчанию. Одна сборка на корень соединения (TypeormRootModule) и CLI миграций — калибровка маски у них не разъедется. Маска bind-параметров — аргумент:

import { createSqlParameterMask, createTypeormLogger } from '@application-kit/typeorm/logging'

const maskSqlParameters = createSqlParameterMask({
  // Порог усечения строки; по умолчанию 256 — user-agent сессии доезжает целиком, дамп обрезается.
  maxValueLength: 256,
  // Формы потребителя проверяются раньше форм по умолчанию.
  forms: [{ label: 'session-token', pattern: /^[\da-f]{64}$/u }],
})

const logger = createTypeormLogger(pinoParams, maskSqlParameters)

Маска смотрит на форму значения, а не на имя колонки (параметры TypeORM позиционные): по умолчанию скрываются bcrypt-хеши, JWT, hex-хеши от 32 символов, шифротекст AES-GCM из трёх base64-кусков, base32 от 16 символов и base64 от 40 — на месте значения метка [masked:<label>]; значение длиннее порога — префикс с пометкой …[truncated:<длина>]. Остальные значения (email, идентификаторы, числа) остаются в логе: по ним разбирают заявки. Массив параметром обходится поэлементно, объекты (JSONB) — нет. Секрет с формой обычного текста маска не ловит.

DatabaseLogger — отдельный PinoLogger с контекстом DatabaseLogger: контекст живёт в экземпляре, и общий логгер приложения под SQL занимать нельзя. Область пробы открывает вызывающий:

return runAsInfrastructureQuery(async () => this.health.check(indicators))

Соединение с Postgres

DatabaseConfig собирает DataSourceOptions из окружения DB_* (DatabaseEnvironment — класс валидации class-validator): соединение всегда с верификацией сертификата сервера, statement_timeout сессии, стратегия имён TypeormPluralNamingStrategy (зависимость пакета) — под неё пишутся сущности и миграции потребителя. entities, migrations и logger в опциях нет — их добавляет вызывающий.

| Переменная | Назначение | |---|---| | DB_HOST, DB_PORT, DB_DATABASE, DB_USERNAME, DB_PASSWORD | адрес и учётные данные | | DB_SSL_CA_FILE | путь к CA сервера БД — обязателен во всех окружениях; нечитаемый файл роняет процесс на буте, а не отключает проверку | | DB_STATEMENT_TIMEOUT_MS | потолок времени SQL-запроса, мс; не задан — без потолка. Задаётся процессам, обслуживающим пользовательские запросы; миграциям, CLI и консьюмерам — нет: у них легитимно долгие запросы |

Регистрацию и валидацию окружения делает приложение — пакет отдаёт классы и корень соединения:

import { DatabaseConfig, DatabaseEnvironment, TypeormRootModule } from '@application-kit/typeorm/nest'

// @libs-for-dev/nestjs-config-module: конфиг и env-класс — провайдеры, валидация на старте
const databaseConfigsModule = ConfigsModule.forFeature([DatabaseConfig], [DatabaseEnvironment])

TypeormRootModule.forRoot({ imports: [databaseConfigsModule] })

TypeormRootModule.forRoot({ imports, maskSqlParameters? }) — TypeOrmModule.forRootAsync с опциями DatabaseConfig, autoLoadEntities (entities пакетов kit регистрирует их собственный TypeOrmModule.forFeature) и логгером createTypeormLogger. Логгер собирается из параметров глобального LoggerModule nestjs-pino (PARAMS_PROVIDER_TOKEN): он обязан быть в графе процесса.

createDataSourceOptions({ maxQueryExecutionTime }) — порог медленного запроса для логгера TypeORM, по умолчанию 1000 мс.

CLI миграций

createCliDataSource собирает DataSource для typeorm -d без Nest-контейнера: опции DatabaseConfig, миграции и entities продукта глобами и таблицы пакетов — их объекты orm (PackageOrmInterface: классы entities и миграций).

import { orm as mailerOrm } from '@application-kit/mailer'
import { orm as outboxOrm } from '@application-kit/transaction-outbox'
import { createCliDataSource, DatabaseConfig, DatabaseEnvironment } from '@application-kit/typeorm/nest'

export default createCliDataSource({
  app: { buildDirs: ['modules', 'libs'], sourceDirs: ['src'] },
  config: new DatabaseConfig(exposeObjectWithValidation(process.env, DatabaseEnvironment)),
  logger: typeormQueryLogger,
  packages: [outboxOrm, mailerOrm],
})

Каталоги — относительно cwd CLI; внутри каждого миграции ищутся по **/typeorm/migrations/, entities — по **/typeorm/entities/. sourceDirs — исходники под ts-node: миграции .ts/.js и entities (по ним migration:generate сравнивает схему). buildDirs — скомпилированный код образа: только миграции .js (рядом лежат .d.ts), entities для migration:run не нужны. Глоб по корню образа не годится — он нашёл бы миграции пакетов в node_modules дважды (ESM и CJS), поэтому пакеты отдают классы. logger обязателен: без него CLI пишет дефолтным логгером TypeORM простым текстом, мимо маски bind-параметров (/logging).