https://github.com/bewinxed/pg-typesafe-triggers
Declarative, Typesafe triggers for Prisma Postgres
https://github.com/bewinxed/pg-typesafe-triggers
Last synced: about 1 year ago
JSON representation
Declarative, Typesafe triggers for Prisma Postgres
- Host: GitHub
- URL: https://github.com/bewinxed/pg-typesafe-triggers
- Owner: Bewinxed
- Created: 2025-05-19T04:12:07.000Z (about 1 year ago)
- Default Branch: main
- Last Pushed: 2025-05-26T13:41:55.000Z (about 1 year ago)
- Last Synced: 2025-06-02T03:22:59.259Z (about 1 year ago)
- Language: TypeScript
- Size: 8.41 MB
- Stars: 1
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
README
# ๐ฏ pg-typesafe-triggers
[](https://github.com/bewinxed/pg-typesafe-triggers/actions/workflows/ci.yml)
[](https://badge.fury.io/js/pg-typesafe-triggers)
[](https://www.npmjs.com/package/pg-typesafe-triggers)




A friendly TypeScript library that brings the power of PostgreSQL triggers to your Prisma workflow! ๐
Never write SQL trigger syntax again - define your database triggers using a beautiful, type-safe API that integrates seamlessly with your existing Prisma schema.
## โจ Features
- ๐ **Universal Compatibility**: Works with any Prisma client, including custom generated clients
- ๐ก๏ธ **Fully Typesafe**: Leverages your specific Prisma schema for complete type safety
- ๐จ **Generic Design**: No hardcoding of model names or fields - it just works with YOUR schema!
- ๐ **Fluent Builder API**: Create triggers using a chainable, intuitive API that feels natural
- ๐ฏ **Typesafe Condition Building**: Write trigger conditions with full TypeScript intellisense
- ๐ฌ **Real-time Notifications**: Subscribe to database changes with type-safe payload handling
- ๐ **Multiple Approaches**: Use individual triggers or a centralized registry - your choice!
- ๐ **Zero SQL Required**: No need to remember PostgreSQL trigger syntax ever again
## ๐ฆ Installation
```bash
npm install pg-typesafe-triggers
# or
yarn add pg-typesafe-triggers
# or
pnpm add pg-typesafe-triggers
# or
bun add pg-typesafe-triggers
```
**Prerequisites:**
- Prisma v4.0 or later
- PostgreSQL database
- `postgres` package (for database connections)
## ๐ Quick Start
Choose your adventure! We offer two delightful ways to work with triggers:
### ๐ฏ Approach 1: Fluent Builder API
Perfect for when you want fine-grained control over individual triggers:
```typescript
import { createTriggers } from 'pg-typesafe-triggers';
import { PrismaClient } from '@prisma/client';
const prisma = new PrismaClient();
const triggers = createTriggers(process.env.DATABASE_URL!);
// Create a beautiful, type-safe trigger โจ
const itemTrigger = triggers
.for('item') // ๐ Your model names are auto-completed!
.withName('notify_item_changes')
.after()
.on('INSERT', 'UPDATE')
.when((c) => c.NEW('status').eq('completed')) // ๐ Type-safe conditions!
.notify('item_updates')
.build();
// Set it up and start listening
await itemTrigger.setup();
await itemTrigger.listen();
// React to changes in real-time! ๐
itemTrigger.subscribe((event) => {
console.log(`Item ${event.data.name} is now ${event.data.status}!`);
});
// Your trigger fires automatically when conditions are met
await prisma.item.create({
data: {
name: 'Important Task',
status: 'completed' // This will trigger our notification!
}
});
```
### ๐ Approach 2: Registry Pattern
Ideal for managing multiple triggers across your application:
```typescript
import { createTriggers } from 'pg-typesafe-triggers';
import { PrismaClient } from '@prisma/client';
const prisma = new PrismaClient();
const triggers = createTriggers(process.env.DATABASE_URL!);
// Create a registry to organize all your triggers ๐
const registry = triggers.registry();
// Add multiple models with their triggers in one go!
registry
.add('item', {
events: ['INSERT', 'UPDATE', 'DELETE'],
timing: 'AFTER',
forEach: 'ROW',
notify: 'item_events'
})
.add('user', {
events: ['INSERT', 'UPDATE'],
timing: 'AFTER',
forEach: 'ROW',
notify: 'user_events'
})
.add('order', {
events: ['INSERT'],
timing: 'AFTER',
forEach: 'ROW',
when: (c) => c.NEW('status').eq('confirmed'),
notify: 'confirmed_orders'
});
// Set up everything with one command! ๐ช
await registry.setup();
await registry.listen();
// Subscribe to all your channels elegantly
registry.on('item', (event) => {
console.log(`Item event: ${event.operation} on ${event.data.name}`);
});
registry.on('user', (event) => {
console.log(`New user registered: ${event.data.email}`);
});
registry.on('confirmed_orders', (event) => {
console.log(`Order confirmed! Amount: $${event.data.total}`);
// Send confirmation email, update inventory, etc.
});
```
## ๐ค Why pg-typesafe-triggers?
**Before** (writing raw SQL triggers):
```sql
CREATE OR REPLACE FUNCTION notify_item_change() RETURNS TRIGGER AS $$
BEGIN
IF NEW.status = 'completed' THEN
PERFORM pg_notify('item_updates', json_build_object(
'operation', TG_OP,
'data', row_to_json(NEW)
)::text);
END IF;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER item_status_trigger
AFTER INSERT OR UPDATE ON "Item"
FOR EACH ROW
WHEN (NEW.status = 'completed')
EXECUTE FUNCTION notify_item_change();
```
**After** (with pg-typesafe-triggers):
```typescript
triggers
.for('item')
.after()
.on('INSERT', 'UPDATE')
.when((c) => c.NEW('status').eq('completed'))
.notify('item_updates')
.build();
```
โจ **Same result, 10x less code, 100% type-safe!**
## ๐ API Reference
### ๐จ Building Triggers
```typescript
// Fluent Builder API
triggers
.for('modelName') // Your Prisma model (auto-completed!)
.withName('my_trigger') // Optional: custom name
.before() / .after() // Timing
.on('INSERT', 'UPDATE') // Events to watch
.watchColumns('col1', 'col2') // Optional: specific columns
.when(condition) // Optional: conditions
.notify('channel') // Send notifications
.build();
```
### ๐ฏ Condition Builder
```typescript
// Type-safe field comparisons
.when((c) => c.NEW('status').eq('active'))
.when((c) => c.NEW('price').gt(100))
.when((c) => c.OLD('email').ne(c.NEW('email')))
// Boolean logic
.when((c) => c.and(
c.NEW('status').eq('published'),
c.NEW('visibility').eq('public')
))
// Check if field changed
.when((c) => c.changed('status'))
// Raw SQL (escape hatch)
.when('NEW.price > 1000 AND NEW.currency = \'USD\'')
```
### ๐ก Notification Handling
```typescript
// Single trigger
trigger.subscribe((event) => {
console.log(event.operation); // 'INSERT' | 'UPDATE' | 'DELETE'
console.log(event.timestamp); // When it happened
console.log(event.data); // Your typed model data
});
// Registry pattern
registry.on('channel', (event) => {
// Same event structure, fully typed!
});
```
### ๐ฎ Lifecycle Management
```typescript
// Individual triggers
await trigger.setup(); // Create in database
await trigger.listen(); // Start listening
await trigger.stop(); // Stop listening
await trigger.drop(); // Remove from database
// Registry
await registry.setup(); // Set up all triggers
await registry.listen(); // Start all listeners
await registry.stop(); // Stop all listeners
await registry.drop(); // Clean up everything
```
## ๐ก Common Patterns
### Audit Logging
```typescript
triggers
.for('user')
.after()
.on('UPDATE')
.when((c) => c.changed('email'))
.notify('audit_log')
.build();
```
### Status Workflows
```typescript
triggers
.for('order')
.after()
.on('UPDATE')
.when((c) => c.and(
c.OLD('status').eq('pending'),
c.NEW('status').eq('confirmed')
))
.notify('order_confirmed')
.build();
```
### Real-time Updates
```typescript
triggers
.for('message')
.after()
.on('INSERT')
.notify('new_messages')
.build();
```
## ๐จ Troubleshooting
### "Could not access Prisma DMMF"
This warning appears when using Prisma with adapters. Your triggers will still work correctly! The library falls back to smart defaults.
### Notifications not received?
1. Check your PostgreSQL logs for errors
2. Ensure your database user has TRIGGER privileges
3. Verify the channel names match between trigger and listener
4. Try running `SELECT * FROM pg_trigger` to see if triggers were created
### Type errors?
Make sure you're passing your Prisma client type correctly:
```typescript
const triggers = createTriggers(DATABASE_URL);
// ^^^^^^^^^^^^^^ This is important!
```
## ๐ค Contributing
We'd love your help making this library even better!
1. Fork the repository
2. Create your feature branch (`git checkout -b feature/amazing-feature`)
3. Run tests (`bun test`)
4. Commit your changes (`git commit -m 'Add some amazing feature'`)
5. Push to the branch (`git push origin feature/amazing-feature`)
6. Open a Pull Request
### Development Setup
```bash
# Clone and install
git clone https://github.com/bewinxed/pg-typesafe-triggers.git
cd pg-typesafe-triggers
bun install
# Run tests
bun test
# Build
bun run build
```
## ๐ License
MIT ยฉ [bewinxed](https://github.com/bewinxed)
---
Made with ๐ by developers who were tired of writing PostgreSQL trigger syntax
If this library helped you, consider giving it a โญ๏ธ
Report Bug
ยท
Request Feature
ยท
Join Discussion