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

typeorm-procedure-kit

v1.4.0

Published

TypeScript toolkit for TypeORM-based Oracle and PostgreSQL apps: stored procedure calls, raw SQL transactions, database notifications, serializers, and a bundled typed TypeORM 0.3.28 fork

Downloads

652

Readme


Краткая сводка

typeorm-procedure-kit — TypeScript toolkit для Node.js сервисов, которые работают с Oracle или PostgreSQL stored procedures, raw SQL transactions, database notifications и TypeORM-style entity API.

| Поле | Значение | | ---------------------- | ---------------------------------------------------------------------------------------------------------------- | | npm package | typeorm-procedure-kit | | Runtime | Node.js >=20 | | Module formats | ESM и CommonJS | | Поддерживаемые базы | PostgreSQL через pg; Oracle через oracledb | | Main API | TypeOrmProcedureKit | | NestJS API | typeorm-procedure-kit/nestjs | | TypeORM-compatible API | typeorm-procedure-kit/typeorm | | Entity extension API | typeorm-procedure-kit/typeorm-extend | | Основные темы | stored procedures, raw SQL transactions, LISTEN/NOTIFY, Oracle CQN, serializers, TypeORM-compatible repositories |

Сценарии использования

Используйте пакет, если сервису нужны одна или несколько возможностей:

  • Вызов Oracle packages или PostgreSQL schema procedures с учетом database metadata.
  • Выполнение raw SQL через тот же transaction и error-handling flow.
  • Подписка на PostgreSQL LISTEN/NOTIFY или Oracle Continuous Query Notification с восстановлением subscriptions после обрыва соединения.
  • Обновление procedure metadata во время работы через database change notifications.
  • Нормализация регистра result keys для raw rows и TypeORM-compatible entity column names.
  • Встроенный TypeORM-compatible API без установки upstream typeorm.
  • Переиспользование базовой entity metadata между Oracle и PostgreSQL entity variants.

Карта API

| Задача | API | Import path | | ------------------------------------- | ------------------------------------------------------ | -------------------------------------- | | Инициализация доступа к базе | new TypeOrmProcedureKit(config), initDatabase() | typeorm-procedure-kit | | Вызов stored procedure | db.call<T>(name, params, options?) | typeorm-procedure-kit | | Выполнение raw SQL transaction | db.callSqlTransaction<T>(sql, params?, options?) | typeorm-procedure-kit | | Подписка на database notifications | db.makeNotify<T>(options, oracleOptions?) | typeorm-procedure-kit | | Отписка от notifications | db.unlistenNotify(channel) | typeorm-procedure-kit | | Регистрация serializers | db.setSerializer(), db.deleteSerializer() | typeorm-procedure-kit | | Доступ к DataSource или EntityManager | db.dataSource, db.getEntityManager() | typeorm-procedure-kit | | NestJS integration | TypeOrmProcedureKitNestModule и injection decorators | typeorm-procedure-kit/nestjs | | TypeORM-compatible APIs | Entity, Column, DataSource, Repository | typeorm-procedure-kit/typeorm | | Расширение entity metadata | ExtendEntity, ExtendColumn | typeorm-procedure-kit/typeorm-extend |

Кратко

| Область | Что входит | | -------------- | --------------------------------------------------------------------------------------------- | | Процедуры | Вызов хранимых процедур с учетом метаданных для Oracle и PostgreSQL packages/schemas. | | SQL | Выполнение SQL через тот же транзакционный поток, что и вызовы процедур. | | Уведомления | Поддержка PostgreSQL LISTEN/NOTIFY и Oracle Continuous Query Notification. | | Case strategy | Общие правила регистра для native result keys и встроенных TypeORM-compatible column names. | | Сериализация | Встроенные и пользовательские сериализаторы значений из базы. | | NestJS | Global dynamic module и точечные injection decorators для публичных runtime methods. | | TypeORM API | Встроенные TypeORM-совместимые экспорты со строгими локальными типами repository и query API. | | Идентификаторы | TypeORM-compatible query builders по умолчанию не оборачивают identifiers в двойные кавычки. |

Автор и сопровождающий: Paul Budanov.

Что входит в пакет

  • TypeOrmProcedureKit как основной API времени выполнения для инициализации базы, вызова процедур, SQL-запросов напрямую, уведомлений, сериализаторов и корректного завершения работы.
  • Общий контракт адаптеров для Oracle и PostgreSQL.
  • Автоматическая загрузка метаданных процедур из database packages/schemas.
  • Обновление метаданных процедур во время работы через уведомления базы.
  • Встроенная case-strategy для native result keys и TypeORM-compatible column names: camelCase, lowerCase, snakeCase.
  • Встроенные и пользовательские сериализаторы значений из базы.
  • Интеграция с NestJS через global dynamic module и decorators для инжекта отдельных публичных методов.
  • Встроенный TypeORM-совместимый экспорт для Oracle/PostgreSQL проектов; TypeORM уже включен в пакет с type fixes для строгих TypeScript-проектов.
  • typeorm-extend декораторы для наследования и переопределения метаданных entities.
  • Экранирование identifiers отключено по умолчанию для встроенного TypeORM-compatible DataSource, поэтому generated SQL избегает случайных двойных кавычек вокруг table и column names, пока вы явно не включите escaping.

Требования

  • Node.js >=20
  • Любой package manager, работающий с npm registry:
    • npm
    • Yarn
    • pnpm
  • TypeScript с включенными decorators, если используются entities
  • Драйвер для целевой базы:
    • PostgreSQL: pg
    • Oracle: oracledb
  • Опциональная streaming-зависимость для PostgreSQL: pg-query-stream; она нужна только при вызове публичных stream API, например SelectQueryBuilder.stream() или QueryRunner.stream().
  • Опционально для NestJS: @nestjs/common и @nestjs/core

Установка

Используйте удобный package manager:

| Package manager | Команда | | --------------- | ----------------------------------- | | npm | npm install typeorm-procedure-kit | | Yarn | yarn add typeorm-procedure-kit | | pnpm | pnpm add typeorm-procedure-kit |

Установите драйвер для нужной базы.

PostgreSQL:

| Package manager | Команда | | --------------- | ---------------- | | npm | npm install pg | | Yarn | yarn add pg | | pnpm | pnpm add pg |

Oracle:

| Package manager | Команда | | --------------- | ---------------------- | | npm | npm install oracledb | | Yarn | yarn add oracledb | | pnpm | pnpm add oracledb |

Установите pg-query-stream только если используете PostgreSQL streaming:

| Package manager | Команда | | --------------- | ----------------------------- | | npm | npm install pg-query-stream | | Yarn | yarn add pg-query-stream | | pnpm | pnpm add pg-query-stream |

Быстрый старт

Минимальный PostgreSQL пример инициализирует kit, вызывает одну настроенную процедуру и освобождает ресурсы:

import { TypeOrmProcedureKit } from 'typeorm-procedure-kit';
import type { IModuleConfig, ILoggerModule } from 'typeorm-procedure-kit';

const logger: ILoggerModule = {
  error: console.error,
  log: console.log,
  warn: console.warn,
  debug: console.debug,
  verbose: console.debug,
};

const settings: IModuleConfig = {
  logger,
  config: {
    type: 'postgres',
    parseInt8AsBigInt: true,
    master: {
      host: 'localhost',
      port: 5432,
      username: 'app',
      password: 'secret',
      database: 'app_db',
    },
    poolSize: 10,
    packagesSettings: {
      packages: ['billing'],
      procedureObjectList: {
        findInvoices: 'billing.find_invoices',
      },
    },
  },
};

const db = new TypeOrmProcedureKit(settings);

await db.initDatabase();

try {
  const invoices = await db.call<{ invoiceId: number }>(
    'billing.find_invoices',
    { customerId: 42 }
  );
  console.log(invoices);
} finally {
  await db.destroy();
}

Точки импорта

import { TypeOrmProcedureKit } from 'typeorm-procedure-kit';
import type { IModuleConfig } from 'typeorm-procedure-kit';

import { TypeOrmProcedureKitNestModule } from 'typeorm-procedure-kit/nestjs';

import { Entity, Column, PrimaryColumn } from 'typeorm-procedure-kit/typeorm';

import {
  ExtendColumn,
  ExtendEntity,
} from 'typeorm-procedure-kit/typeorm-extend';

Root import экспортирует kit, публичные типы, constants и utilities. TypeORM уже включен в этот пакет: decorators, DataSource, EntityManager, repositories, query builders и связанные классы экспортируются из ./typeorm. Для entities, которыми управляет kit, импортируйте TypeORM API из typeorm-procedure-kit/typeorm, а не из отдельного upstream-пакета typeorm.

| Import path | Для чего использовать | | -------------------------------------- | ---------------------------------------------------------------------------------- | | typeorm-procedure-kit | TypeOrmProcedureKit, публичные типы, utilities, constants | | typeorm-procedure-kit/nestjs | NestJS module, service, method injection decorators | | typeorm-procedure-kit/typeorm | Встроенные TypeORM-compatible decorators, DataSource, repositories, query builders | | typeorm-procedure-kit/typeorm-extend | ExtendEntity, ExtendColumn и связанные metadata extension decorators |

Конфигурация

Настройка выполняется через IModuleConfig:

import type { IModuleConfig, ILoggerModule } from 'typeorm-procedure-kit';

const logger: ILoggerModule = {
  error: (message, ...optionalParams) =>
    console.error(message, ...optionalParams),
  log: (message, ...optionalParams) => console.log(message, ...optionalParams),
  warn: (message, ...optionalParams) =>
    console.warn(message, ...optionalParams),
  debug: (message, ...optionalParams) =>
    console.debug(message, ...optionalParams),
  verbose: (message, ...optionalParams) =>
    console.debug(message, ...optionalParams),
};

const config: IModuleConfig = {
  logger,
  isRegisterShutdownHandlers: true,
  config: {
    type: 'postgres',
    master: {
      host: 'localhost',
      port: 5432,
      username: 'app',
      password: 'secret',
      database: 'app_db',
    },
    poolSize: 10,
    parseInt8AsBigInt: true,
    appName: 'procedure-service',
    callTimeout: 30_000,
    outKeyTransformCase: 'camelCase',
    isNeedRegisterDefaultSerializers: true,
    packagesSettings: {
      packages: ['billing'],
      procedureObjectList: {
        createInvoice: 'billing.create_invoice',
        findInvoices: 'billing.find_invoices',
      },
      isNeedDynamicallyUpdatePackagesInfo: true,
      listenEventName: 'package_changed',
    },
  },
  entity: {
    isNeedEntitySync: false,
    entityPath: ['dist/entities/*.js'],
  },
  migration: {
    isNeedMigrationStart: false,
    migrationPath: ['dist/migrations/*.js'],
  },
};

Общие параметры:

  • master: учетные данные основного подключения к базе.
  • slaves: read replicas для TypeORM replication.
  • poolSize: размер пула соединений.
  • appName: имя приложения для драйвера, если он это поддерживает.
  • callTimeout: порог логирования медленных запросов для TypeORM.
  • parseInt8AsBigInt: PostgreSQL-only параметр, который передается во встроенный драйвер как parseInt8. Когда значение равно true, node-postgres разбирает int8 как JavaScript numbers вместо strings; значения выше Number.MAX_SAFE_INTEGER могут потерять точность, несмотря на название параметра.
  • outKeyTransformCase: регистр ключей результата, по умолчанию camelCase.
  • isNeedRegisterDefaultSerializers: включает стандартные date/time serializers.
  • isNeedClientNotificationInit: режим Oracle CQN по умолчанию. true включает client-initiated notifications, false используется вместе с cqnPort для server callback notifications.
  • packagesSettings: packages/schemas и процедуры, доступные через call().

packagesSettings.packages нужно указывать в lowercase. Имена процедур при вызове нормализуются внутри библиотеки.

В примерах для PostgreSQL слово "package" означает настроенное namespace schema, которое использует kit. В примерах для Oracle это Oracle package.

procedureObjectList используется для разрешения настроенных процедур и, когда настроено несколько packages/schemas, для пропуска процедур вне текущего package. Ключи служат только метками внутри конфигурации; значения должны быть реальными именами процедур. Используйте формат package.procedure или schema.procedure, когда настроено несколько packages. Bare-имя procedure допустимо, когда настроен ровно один package. Runtime вызовы разрешаются по загруженным database names, поэтому используйте db.call('billing.find_invoices') или db.call('find_invoices'), если настроен только один package. Не используйте ключи procedureObjectList как aliases для call().

Если задан packagesSettings.packages и packages array не пустой, initDatabase() также создает подписку на уведомления об изменении packages. Перед использованием этих примеров без изменений настройте источник уведомлений в базе.

Встроенная case-strategy

typeorm-procedure-kit включает встроенную case-strategy для двух мест, где имена из базы встречаются с application code: native result objects и встроенная TypeORM-compatible naming strategy. Настройка задается через config.outKeyTransformCase внутри database config.

Поддерживаемые значения:

| Значение | Ключ из базы | Ключ на выходе | | ----------- | ------------------------------- | -------------- | | camelCase | USER_ID, user_id, user id | userId | | snakeCase | USER_ID, userId, User Id | user_id | | lowerCase | USER_ID, User_Id | user_id |

Если outKeyTransformCase не задан, используется camelCase. Неизвестные значения во время выполнения также откатываются к camelCase.

Пакет создает два strategy objects из одной настройки:

  • NativeStrategy преобразует имена колонок, которые приходят из metadata pg или oracledb, перед возвратом native rows из вызовов процедур и raw SQL. SQL aliases тоже преобразуются, потому что drivers отдают aliases как result metadata.
  • OrmStrategy устанавливается как DataSource naming strategy при инициализации. Она преобразует имена свойств entity перед передачей во встроенную default naming strategy, поэтому generated column names следуют выбранному формату, если decorator не задает explicit name.

Пример:

const config: IModuleConfig = {
  logger,
  config: {
    type: 'postgres',
    master,
    poolSize: 10,
    parseInt8AsBigInt: true,
    outKeyTransformCase: 'snakeCase',
  },
  entity: {
    isNeedEntitySync: false,
    entityPath: ['dist/entities/*.js'],
  },
  migration: {
    isNeedMigrationStart: false,
    migrationPath: ['dist/migrations/*.js'],
  },
};

При такой настройке колонка raw result USER_ID или userId вернется как user_id. При camelCase тот же ключ будет возвращен как userId.

Важные детали:

  • Стратегия меняет output object keys и generated ORM column names. Она не переписывает package names, procedure names, SQL text, bind placeholders, table names, relation names или notification channel names.
  • packagesSettings.packages по-прежнему нужно указывать в lowercase, потому что procedure resolution нормализует configured package/schema names внутри.
  • Raw SQL bind placeholders по-прежнему используют документированный uppercase стиль, например :USER_ID.
  • Явно заданные custom column names в decorators учитываются встроенной default naming strategy, поэтому стандартное TypeORM override behavior сохраняется.
  • lowerCase только приводит входную строку к нижнему регистру. Используйте snakeCase, если нужно разбиение слов, например User Id -> user_id.
  • Преобразованные имена кэшируются на время жизни kit instance, а cache очищается при destroy().

Oracle CQN можно настроить в двух режимах. Server callback mode открывает локальный порт для уведомлений от базы:

const oracleServerCallbackConfig: IModuleConfig['config'] = {
  type: 'oracle',
  master: {
    host: 'localhost',
    port: 1521,
    username: 'APP',
    password: 'secret',
    database: 'ORCLCDB',
  },
  poolSize: 10,
  libraryPath: '/opt/oracle/instantclient',
  cqnPort: 9090,
  isNeedClientNotificationInit: false,
};

Client-initiated mode не требует cqnPort и обычно удобнее, когда база не может подключиться обратно к application host:

const oracleClientInitiatedConfig: IModuleConfig['config'] = {
  type: 'oracle',
  master: {
    host: 'localhost',
    port: 1521,
    username: 'APP',
    password: 'secret',
    database: 'ORCLCDB',
  },
  poolSize: 10,
  libraryPath: '/opt/oracle/instantclient',
  isNeedClientNotificationInit: true,
};

Oracle option clientInitiated в makeNotify() может переопределить это значение для отдельной subscription.

PostgreSQL

import { TypeOrmProcedureKit } from 'typeorm-procedure-kit';
import type { ILoggerModule, IModuleConfig } from 'typeorm-procedure-kit';

const logger: ILoggerModule = {
  error: (message, ...optionalParams) =>
    console.error(message, ...optionalParams),
  log: (message, ...optionalParams) => console.log(message, ...optionalParams),
  warn: (message, ...optionalParams) =>
    console.warn(message, ...optionalParams),
  debug: (message, ...optionalParams) =>
    console.debug(message, ...optionalParams),
  verbose: (message, ...optionalParams) =>
    console.debug(message, ...optionalParams),
};

const settings: IModuleConfig = {
  logger,
  config: {
    type: 'postgres',
    parseInt8AsBigInt: true,
    master: {
      host: 'localhost',
      port: 5432,
      username: 'app',
      password: 'secret',
      database: 'app_db',
    },
    poolSize: 10,
    packagesSettings: {
      packages: ['api'],
      procedureObjectList: {
        getUser: 'api.get_user',
        searchUsers: 'api.search_users',
      },
    },
  },
};

const db = new TypeOrmProcedureKit(settings);

await db.initDatabase();

const users = await db.call<{ id: number; fullName: string }>(
  'api.search_users',
  {
    query: 'Paul',
  }
);

await db.destroy();

Для PostgreSQL вызов процедуры формируется так:

CALL "package"."procedure"($1, $2, ...)

Выходные аргументы PostgreSQL типа refcursor читаются и закрываются внутри той же транзакции.

Oracle

import { TypeOrmProcedureKit } from 'typeorm-procedure-kit';
import type { ILoggerModule, IModuleConfig } from 'typeorm-procedure-kit';

const logger: ILoggerModule = {
  error: (message, ...optionalParams) =>
    console.error(message, ...optionalParams),
  log: (message, ...optionalParams) => console.log(message, ...optionalParams),
  warn: (message, ...optionalParams) =>
    console.warn(message, ...optionalParams),
  debug: (message, ...optionalParams) =>
    console.debug(message, ...optionalParams),
  verbose: (message, ...optionalParams) =>
    console.debug(message, ...optionalParams),
};

const settings: IModuleConfig = {
  logger,
  config: {
    type: 'oracle',
    master: {
      host: 'localhost',
      port: 1521,
      username: 'APP',
      password: 'secret',
      database: 'ORCLCDB',
    },
    poolSize: 10,
    libraryPath: '/opt/oracle/instantclient',
    cqnPort: 9090,
    isNeedClientNotificationInit: false,
    packagesSettings: {
      packages: ['billing'],
      procedureObjectList: {
        findInvoices: 'billing.find_invoices',
      },
    },
  },
};

const db = new TypeOrmProcedureKit(settings);

await db.initDatabase();

const invoices = await db.call<{ invoiceId: number }>('billing.find_invoices', {
  customerId: 100,
});

await db.destroy();

Для Oracle вызов процедуры формируется так:

BEGIN PACKAGE.PROCEDURE(:arg1, :arg2); END;

Результаты Oracle REF CURSOR считываются потоково и возвращаются как строки результата.

Вызов процедур

call<T>() требует config.packagesSettings.

const rows = await db.call<{ id: number; status: string }>(
  'billing.find_invoices',
  { customerId: 42, status: 'OPEN' }
);

Если настроен только один package, имя package можно опустить:

const rows = await db.call('find_invoices', { customerId: 42 });

Правила payload:

  • Object bind выполняется по имени аргумента. Адаптеры также пробуют имя без префикса p_, поэтому p_customer_id можно передать как customer_id.
  • Array bind выполняется по позиции аргумента.
  • Если payload не передан или передан undefined, все non-cursor значения будут привязаны как null.
  • Scalar string/number нельзя передавать как payload процедуры.
await db.call('billing.update_invoice', [42, 'PAID']);

Третий аргумент — массив SQL-команд, которые выполняются перед основным вызовом внутри той же транзакции:

await db.call('billing.recalculate', { invoiceId: 42 }, [
  'SET LOCAL statement_timeout = 30000',
]);

Транзакции с SQL-запросами напрямую

callSqlTransaction<T>() выполняет SQL-запрос напрямую через тот же поток транзакции, логирование, обработку ошибок и освобождение соединения.

Параметры находятся только по плейсхолдерам в верхнем регистре: :PARAM_NAME.

const rows = await db.callSqlTransaction<{ id: number; name: string }>(
  'SELECT id, name FROM users WHERE id = :USER_ID',
  { USER_ID: 7 }
);

PostgreSQL переписывает плейсхолдеры в $1, $2, а Oracle оставляет :PARAM.

Уведомления

PostgreSQL LISTEN/NOTIFY

const channel = await db.makeNotify<{ event: string; object: string }>({
  sql: 'LISTEN package_changed',
  notifyCallback: (payload) => {
    console.log(payload.event, payload.object);
  },
});

await db.unlistenNotify(channel);

PostgreSQL adapter проверяет имя channel, открывает отдельный pg.Client, пытается разобрать JSON payload и восстанавливает listener после ошибок соединения. Также выполняется периодический health-check соединения, поэтому listener может быть восстановлен после тихого сетевого обрыва, когда PostgreSQL не прислал явное событие.

Retry options для восстановления notification subscriptions можно передать вторым аргументом makeNotify() для Oracle, а при прямом использовании adapter API — как notification options адаптера:

  • maxRetries: число попыток восстановления до длинной задержки, default 5.
  • retryDelayMs: задержка между обычными попытками восстановления, default 30000.
  • retryAfterMaxDelayMs: задержка после исчерпания maxRetries перед новым циклом попыток, default 1800000.

Oracle Continuous Query Notification

import oracledb from 'oracledb';

const channel = await db.makeNotify<Array<{ ID: number }>>(
  {
    sql: 'SELECT ID, STATUS FROM BILLING.INVOICES',
    notifyCallback: (rows) => {
      console.log(rows);
    },
  },
  {
    operations: oracledb.CQN_OPCODE_ALL_OPS,
    qos: oracledb.SUBSCR_QOS_ROWIDS,
    timeout: 60 * 60,
    clientInitiated: true,
  }
);

await db.unlistenNotify(channel);

Oracle создает внутреннее UUID-имя subscription. Когда Oracle возвращает ROWID, adapter читает измененные строки и передает их в callback. Если передать operations как array, создается отдельная CQN subscription на каждую operation; возвращенная строка channel содержит сгенерированные имена subscriptions, и ее можно без изменений передать в unlistenNotify().

Дефолты Oracle notifications:

  • operations: oracledb.CQN_OPCODE_ALL_OPS
  • qos: oracledb.SUBSCR_QOS_ROWIDS
  • timeout: 12 часов
  • clientInitiated: option конкретной subscription, затем общий config, затем false

Если operations передан как array, в нем должно быть меньше четырех operation codes. Для подписки на все операции используйте oracledb.CQN_OPCODE_ALL_OPS, а не ручной список всех операций. Oracle subscriptions также проверяются периодическим health-check и восстанавливаются после CQN deregistration, shutdown events, ошибок соединения или тихого обрыва соединения.

Обновление метаданных packages

Если настроен packagesSettings.packages, initDatabase() загружает метаданные процедур из базы. Также создается подписка на уведомления об изменении packages всякий раз, когда packages array не пустой. У пользователя базы должен быть доступ к источнику уведомлений: PostgreSQL по умолчанию слушает db_object_event, а Oracle читает SOLUTION_ROOT.DB_OBJECT_LOG.

Для PostgreSQL можно переопределить notification channel, когда isNeedDynamicallyUpdatePackagesInfo равен true:

packagesSettings: {
  packages: ['api'],
  procedureObjectList: {
    getUser: 'api.get_user',
  },
  isNeedDynamicallyUpdatePackagesInfo: true,
  listenEventName: 'package_changed',
}

Сериализаторы

Включение встроенных serializers:

const settings = {
  config: {
    // ...
    isNeedRegisterDefaultSerializers: true,
  },
};

Стандартные serializers форматируют:

  • DATE как yyyy-MM-dd
  • TIMESTAMP как yyyy-MM-dd HH:mm:ss Z
  • TIMESTAMP_TZ как yyyy-MM-dd HH:mm:ss Z

Пользовательский serializer:

db.setSerializer({
  serializerType: 'JSON',
  strategy: (value) => JSON.parse(value.toString()),
});

const serializers = db.serializerReadOnlyMapping;

db.deleteSerializer({ serializerType: 'JSON' });
db.deleteAllSerializers();

Поддерживаемые ключи: DATE, TIMESTAMP, TIMESTAMP_TZ, BOOLEAN, CHAR, VARCHAR, JSON, BINARY, XML.

NestJS

import { Logger, Module } from '@nestjs/common';
import { TypeOrmProcedureKitNestModule } from 'typeorm-procedure-kit/nestjs';

@Module({
  imports: [
    TypeOrmProcedureKitNestModule.forRoot({
      logger: new Logger('TypeOrmProcedureKit'),
      config: {
        type: 'postgres',
        parseInt8AsBigInt: true,
        master: {
          host: 'localhost',
          port: 5432,
          username: 'app',
          password: 'secret',
          database: 'app_db',
        },
        poolSize: 10,
      },
    }),
  ],
})
export class AppModule {}

Async setup:

TypeOrmProcedureKitNestModule.forRootAsync({
  useFactory: async () => ({
    logger: new Logger('TypeOrmProcedureKit'),
    config: {
      type: 'postgres',
      parseInt8AsBigInt: true,
      master: {
        host: process.env.DB_HOST ?? 'localhost',
        port: Number(process.env.DB_PORT ?? 5432),
        username: process.env.DB_USER ?? 'app',
        password: process.env.DB_PASSWORD ?? 'secret',
        database: process.env.DB_NAME ?? 'app_db',
      },
      poolSize: 10,
    },
  }),
});

Nest service наследует TypeOrmProcedureKit, вызывает initDatabase() в onModuleInit() и destroy() в onApplicationShutdown().

NestJS entry point также экспортирует точечные decorators для инжекта отдельных публичных методов вместо всего service:

| Decorator | Тип injected function | Делегирует в | | ------------------------------- | ----------------------- | -------------------------------------------- | | @InjectCallProcedure() | TCallProcedure | TypeOrmProcedureKit.call() | | @InjectCallSql() | TCallSql | TypeOrmProcedureKit.callSqlTransaction() | | @InjectMakeNotify() | TMakeNotify | TypeOrmProcedureKit.makeNotify() | | @InjectUnlistenNotify() | TUnlistenNotify | TypeOrmProcedureKit.unlistenNotify() | | @InjectSetSerializer() | TSetSerializer | TypeOrmProcedureKit.setSerializer() | | @InjectDeleteSerializer() | TDeleteSerializer | TypeOrmProcedureKit.deleteSerializer() | | @InjectDeleteAllSerializers() | TDeleteAllSerializers | TypeOrmProcedureKit.deleteAllSerializers() |

import { Injectable } from '@nestjs/common';
import {
  InjectCallProcedure,
  InjectCallSql,
  type TCallProcedure,
  type TCallSql,
} from 'typeorm-procedure-kit/nestjs';

@Injectable()
export class BillingRepository {
  public constructor(
    @InjectCallProcedure()
    private readonly callProcedure: TCallProcedure,
    @InjectCallSql()
    private readonly callSql: TCallSql
  ) {}

  public findInvoices(customerId: number): Promise<Array<{ id: number }>> {
    return this.callProcedure<{ id: number }>('billing.find_invoices', {
      customerId,
    });
  }

  public countInvoices(customerId: number): Promise<Array<{ total: number }>> {
    return this.callSql<{ total: number }>(
      'SELECT COUNT(*) AS total FROM invoices WHERE customer_id = :CUSTOMER_ID',
      { CUSTOMER_ID: customerId }
    );
  }
}

EntityManager и DataSource

Для низкоуровневой работы можно получить TypeORM-compatible объекты:

const manager = await db.getEntityManager('master');

try {
  const rows = await manager.query('SELECT 1 AS value');
  console.log(rows);
} finally {
  await db.releaseEntityManager(manager);
}

const dataSource = db.dataSource;
const adapter = db.databaseAdapter;

getEntityManager() принимает master или slave. databaseAdapter открывает низкоуровневый adapter contract для диагностики и расширенной интеграции.

isRegisterShutdownHandlers регистрирует shutdown handlers в constructor. Если нужно подключить их позже, вызовите db.registerShutdownHandlers().

Встроенный TypeORM-compatible API

Встроенный TypeORM-совместимый API адаптирован для этой библиотеки. TypeORM уже включен в пакет как поддерживаемый fork; версия fork здесь — 0.3.28. Сейчас поддерживаются PostgreSQL и Oracle.

Слой типизации TypeORM был переработан для проектов со строгим TypeScript:

  • entity metadata лучше сохраняет generic-типы сущностей;
  • repository, query builder и entity manager методы имеют более строгие generic return types;
  • common repository inputs вроде FindOptionsWhere, FindManyOptions, FindOneOptions, DeepPartial и QueryPartialEntity согласованы с формой entity, которую экспортирует этот пакет;
  • decorator и metadata argument types адаптированы для database-specific вариантов entity;
  • Column, PrimaryColumn, PrimaryGeneratedColumn, relation decorators и entity schema options раскрывают database-specific surfaces, которые использует kit;
  • Oracle/PostgreSQL column types и query-runner API сужены под поддерживаемые драйверы.

Карты свойств metadata

EntityMetadata.propertiesMap типизирована как EntityPropertiesMap<T> и сохраняет TypeORM-compatible property-path семантику. Callback decorators для Index, Unique, RelationId, RelationCount и EntityOptions.orderBy получают эту карту на runtime, поэтому их значения по-прежнему резолвятся в property paths сущности. orderBy callbacks вычисляются после создания propertiesMap.

EntityMetadata.databasePropertiesMap — отдельная EntityDatabasePropertiesMap<T>. Это column-only карта: leaf values содержат database column names или database paths после применения явных @Column({ name }) options и правил naming strategy. Relation virtual join columns намеренно исключены из этой карты.

import { Column, Entity, PrimaryColumn } from 'typeorm-procedure-kit/typeorm';

@Entity()
class User {
  @PrimaryColumn()
  id!: number;

  @Column({ name: 'user_id' })
  userId!: string;
}

// metadata.propertiesMap.userId === 'userId'
// metadata.databasePropertiesMap.userId === 'user_id'

Для callback decorators user.userId в @Index((user) => [user.userId]) по-прежнему означает property path userId; используйте databasePropertiesMap только там, где из metadata напрямую нужны database column names или paths.

Kit устанавливает isQuotingDisabled: true при инициализации DataSource. Query builder по умолчанию не экранирует identifiers, поэтому generated SQL не получает случайные "USERS" или "CREATED_AT", когда схема базы ожидает обычные uppercase identifiers. При необходимости quoting можно включить через enableEscaping() или принудительно экранировать отдельный identifier через escape(name, true).

Этот режим quoting относится к SQL, который генерируют entity, repository и query builder. Вызовы процедур и notification channels проходят через отдельные adapter paths: identifiers валидируются через SqlIdentifier и экранируются или форматируются там, где этого требует целевая база.

Будущее направление ORM

Текущая точка входа typeorm-procedure-kit/typeorm остается TypeORM-compatible и сегодня является поддерживаемым entity API пакета. Дальнейшее развитие планируется вести в сторону собственной ORM-системы библиотеки вместо долгосрочной опоры на встроенный fork TypeORM.

Цель этого направления — сохранить database workflow вокруг возможностей, которые уже контролирует пакет: metadata хранимых процедур, адаптеры Oracle и PostgreSQL, явную работу с identifiers, repository/query API и строгие TypeScript-типы. Такой переход предполагается делать постепенно и описывать через публичные точки входа, без обещаний конкретной даты релиза.

Расширяющие декораторы TypeORM

./typeorm-extend экспортирует decorators для переиспользования базовых метаданных entity и переопределения options для database-specific вариантов.

import {
  Column,
  Entity,
  PrimaryGeneratedColumn,
} from 'typeorm-procedure-kit/typeorm';

abstract class SoftDelete {
  @Column({
    type: Date,
    name: 'DELETED_AT',
    nullable: true,
    comment: 'Дата мягкого удаления',
  })
  protected abstract readonly deletedAt: Date | null;

  @Column({
    type: Number,
    name: 'IS_DELETED',
    nullable: false,
    default: 0,
    comment: 'Флаг мягкого удаления',
  })
  protected abstract readonly isDeleted: number;
}

@Entity({
  name: 'OUTBOUND_MESSAGES',
  schema: 'APP_CORE',
  comment: 'Сообщения для внешней доставки',
})
export abstract class OutboundMessage extends SoftDelete {
  @PrimaryGeneratedColumn('uuid', {
    name: 'UUID',
    comment: 'Идентификатор записи',
  })
  protected abstract readonly uuid: string | undefined | null;

  @Column({
    type: Date,
    name: 'CREATED_AT',
    nullable: false,
    comment: 'Дата создания',
  })
  protected abstract readonly createdAt: Date;

  @Column({
    type: Date,
    name: 'SEND_AT',
    nullable: true,
    comment: 'Плановая дата отправки',
  })
  protected abstract readonly sendAt: Date | null;

  @Column({
    type: Number,
    name: 'RECIPIENT_ID',
    nullable: false,
    comment: 'Идентификатор получателя',
  })
  protected abstract readonly recipientId: number;

  @Column({
    type: String,
    name: 'CONTENT',
    length: 4000,
    nullable: true,
    comment: 'Текст сообщения',
  })
  protected abstract readonly content: string | null;

  @Column({
    type: String,
    name: 'DELIVERY_STATUS',
    default: 'CREATED',
    nullable: false,
    comment: 'Статус доставки',
  })
  protected abstract readonly deliveryStatus: string;
}
import {
  ExtendColumn,
  ExtendEntity,
  ExtendPrimaryGeneratedColumn,
} from 'typeorm-procedure-kit/typeorm-extend';

import { OutboundMessage } from '../general/outbound-message.entity.js';

@ExtendEntity()
export class OutboundMessageOracle extends OutboundMessage {
  @ExtendPrimaryGeneratedColumn()
  declare public readonly uuid: string | null | undefined;

  @ExtendColumn({ type: 'date', default: 'SYSDATE' })
  declare public readonly createdAt: Date;

  @ExtendColumn({ type: 'date' })
  declare public readonly sendAt: Date | null;

  @ExtendColumn({ type: 'number' })
  declare public readonly recipientId: number;

  @ExtendColumn({ type: 'varchar2' })
  declare public readonly content: string | null;

  @ExtendColumn({ type: 'varchar2' })
  declare public readonly deliveryStatus: string;

  @ExtendColumn({ type: 'date' })
  declare public readonly deletedAt: Date | null;

  @ExtendColumn({ type: 'number' })
  declare public readonly isDeleted: number;
}
import {
  ExtendColumn,
  ExtendEntity,
  ExtendPrimaryGeneratedColumn,
} from 'typeorm-procedure-kit/typeorm-extend';

import { OutboundMessage } from '../general/outbound-message.entity.js';

@ExtendEntity()
export class OutboundMessagePostgres extends OutboundMessage {
  @ExtendPrimaryGeneratedColumn()
  declare public readonly uuid: string | null | undefined;

  @ExtendColumn({ type: 'timestamp', default: 'CURRENT_TIMESTAMP' })
  declare public readonly createdAt: Date;

  @ExtendColumn({ type: 'timestamp' })
  declare public readonly sendAt: Date | null;

  @ExtendColumn({ type: 'int8' })
  declare public readonly recipientId: number;

  @ExtendColumn({ type: 'varchar' })
  declare public readonly content: string | null;

  @ExtendColumn({ type: 'varchar' })
  declare public readonly deliveryStatus: string;

  @ExtendColumn({ type: 'timestamp' })
  declare public readonly deletedAt: Date | null;

  @ExtendColumn({ type: 'int2' })
  declare public readonly isDeleted: number;
}

Базовые метаданные должны уже существовать через @Entity, @Column, @PrimaryColumn или @PrimaryGeneratedColumn.

Завершение работы

await db.destroy();

destroy():

  • отписывает notifications;
  • уничтожает пул соединений DataSource;
  • очищает procedure и naming caches;
  • выбрасывает AggregateError, если часть очистки завершилась ошибкой.

isRegisterShutdownHandlers: true регистрирует обработчики сигналов процесса, которые автоматически вызывают destroy().

Важные runtime особенности

  • PostgreSQL serializer глобально переопределяет pg.Result.prototype.parseRow.
  • Oracle serializer глобально устанавливает oracledb.fetchTypeHandler.
  • Oracle adapter устанавливает oracledb.outFormat = oracledb.OUT_FORMAT_OBJECT.
  • Метаданные процедур загружаются из базы во время initDatabase(), поэтому packages должны существовать и быть видимыми пользователю базы.
  • call() нельзя использовать без packagesSettings.
  • Плейсхолдеры для SQL-запросов напрямую должны быть в верхнем регистре, например :USER_ID.
  • PostgreSQL parseInt8AsBigInt следует поведению bundled TypeORM/Postgres parseInt8: true возвращает JavaScript numbers для int8, а не native bigint.

Частые ошибки

  • TypeOrmProcedureKit is not initialized: вызовите await initDatabase().
  • Procedure packages are not configured: настройте config.packagesSettings перед использованием call().
  • Package "... " or process "... " not found: это ошибка адаптера для неизвестной процедуры; проверьте имена packages, procedureObjectList и доступность метаданных в базе.
  • Payload for call procedure must be an object or array or undefined or null: не передавайте скалярный payload в call().
  • Unsafe SQL identifier for ...: имена процедур, cursors или notification channels должны соответствовать поддерживаемому identifier pattern до того, как adapter встроит их в SQL.
  • Результаты базы с nonzero error_code или err_code превращаются в ServerError.

Лицензия

MIT.