@nathapp/nestjs-notify-prisma
v1.1.1
Published
Prisma adapter for @nathapp/nestjs-notify
Readme
@nathapp/nestjs-notify-prisma
Prisma adapter for @nathapp/nestjs-notify. Binds the core package's repository
tokens to AbstractPrismaRepository-based implementations.
Install
npm i @nathapp/nestjs-notify-prisma
npm i -D prisma && npx prisma initSchema
Append the NotificationPreference, DeliveryLog and NotificationTemplate
models from the packaged fragment
node_modules/@nathapp/nestjs-notify-prisma/prisma/notify.prisma to your app's
schema.prisma. The fragment carries no datasource or generator block — it
is models only. Every DateTime in it uses @db.Timestamptz(6) (PostgreSQL
timestamptz); bare DateTime is not supported.
PostgreSQL and MySQL. The fragment is authored for PostgreSQL 16 and also
supports MySQL 8.0. For MySQL, assemble it with @db.Timestamptz(6) rewritten
to @db.DateTime(6) (the only change; see the @nathapp/nestjs-iam-prisma
README, "Schema assembly and database support") and keep the MySQL server and
sessions on UTC (time_zone = '+00:00'), because DATETIME(6) stores no
timezone. Upgrading a database built from an earlier fragment: on MySQL apply
prisma/sql/long-text-columns.mysql.sql; on PostgreSQL apply
prisma/sql/recipient-varchar-512.postgresql.sql (delivery_logs.recipient
becomes VARCHAR(512) so its index fits MySQL's key limit).
Keep the @@unique([userId, tenantId, channel]) constraint on
NotificationPreference — PrismaPreferenceRepository.upsertByUserAndChannel
depends on it.
Register
import { Module } from '@nestjs/common';
import { PrismaPg } from '@prisma/adapter-pg'; // MySQL: PrismaMariaDb from '@prisma/adapter-mariadb' (host/port/user/password/database, no connectionString)
import { PrismaClient } from './generated/prisma/client'; // your generator `output`
import { PrismaModule } from '@nathapp/nestjs-prisma';
import { NotifyPrismaModule } from '@nathapp/nestjs-notify-prisma';
@Module({
imports: [
PrismaModule.forRoot({
client: PrismaClient,
clientOptions: { adapter: new PrismaPg({ connectionString: process.env.DATABASE_URL! }) },
transaction: true,
}),
NotifyPrismaModule.register(),
],
})
export class AppModule {}register() takes an optional options object:
| Option | Type | Default | Meaning |
|---|---|---|---|
| global | boolean | true | Register as a global module so the repository tokens resolve anywhere. |
register() is the only import form for this adapter. There is no registerAsync.
If you already wrap this adapter in @Global()
Pass global: false. Otherwise the adapter's own global exports are injected
in addition to your override, and last-registration-wins silently decides
which implementation is used:
import { Global, Module } from '@nestjs/common';
import { TEMPLATE_REPOSITORY } from '@nathapp/nestjs-notify';
import { NotifyPrismaModule } from '@nathapp/nestjs-notify-prisma';
import { AuditedTemplateRepository } from './audited-template.repository';
@Global()
@Module({
imports: [NotifyPrismaModule.register({ global: false })],
providers: [{ provide: TEMPLATE_REPOSITORY, useClass: AuditedTemplateRepository }],
exports: [TEMPLATE_REPOSITORY],
})
export class PrismaOverridesModule {}global: false only helps if the adapter is registered once. A second, global
register() elsewhere in the app keeps its global exports, and global: false
here does not remove them — so the collision this section exists to prevent is
still there. Register the adapter in exactly one place.
The global option carries no documentation in the published type definitions
(removeComments: true strips the JSDoc from the emitted .d.ts), so this README
is where it is described. The options object is typed as PrismaAdapterModuleOptions,
which is exported from this package if you need to name the type.
Transactions
Multi-write flows need PrismaModule.forRoot({ client: PrismaClient, transaction: true })
with PrismaClient imported from your generated client (Prisma 7 prisma-client generator).
Every repository resolves its client per operation from the TRANSACTION_MANAGER
token, and the two PrismaModule registration paths fail differently when
transaction is omitted:
PrismaModule.forRoot()bindsTRANSACTION_MANAGERonly whentransaction: trueis set. Without it nothing is bound, the repositories cannot resolve their dependency, and Nest fails at boot.PrismaModule.forRootAsync()always binds the token, but with a manager whoserun()andgetClient()throw and whoseisInTransaction()returnsfalse, so the app boots and fails on first use instead.
Neither path is a silent passthrough: a real transaction manager either joins the transaction already in scope or opens a Prisma interactive transaction.
Both of those are PrismaModule from @nathapp/nestjs-prisma. These adapters
expose only the synchronous register(); they have no registerAsync.
Tokens bound
| Token | Implementation |
|---|---|
| TEMPLATE_REPOSITORY | PrismaTemplateRepository |
| PREFERENCE_REPOSITORY | PrismaPreferenceRepository |
| DELIVERY_LOG_REPOSITORY | PrismaDeliveryLogRepository |
