Database channel
This subpackage gives notify its durable, queryable channel: import it when a notification needs a persisted, readable history, not just a mail that went out or a live push.
import notifydatabase "github.com/gp-system/notify/database"
Database
notify/database (+notify/database/bunx) is the durable, queryable side of a notification: every Databasable notification lands in a notifications table, so a recipient gets a persisted, "notification inbox" pattern (the Laravel-notifications-esque part of the module) on top of whatever else fires. It builds on dbx, the same way the rest of the kit's persistence does.
store := notifydatabase.NewStore(pg.NewDB(pool)) // pgx
store := notifydatabasebunx.NewStore(bunDB) // bun, via notify/database/bunx
channel := notifydatabase.NewChannel(store)
Record and Store
Persists Databasable notifications into a notifications table:
CREATE TABLE notifications (
id uuid PRIMARY KEY,
recipient_id text NOT NULL,
name text NOT NULL,
payload jsonb NOT NULL DEFAULT '{}'::jsonb,
read_at timestamptz,
created_at timestamptz NOT NULL DEFAULT now()
);
No notifiable_type column (the kit has no polymorphic user model, a recipient is just a string id), no updated_at (rows only ever gain read_at). The channel requires Recipient.ID; an on-demand send (no ID) errors on this channel: name only notify.ChannelMail in Via for those.
The Store interface covers the universal 90%: Insert, List (ListOptions{UnreadOnly, Limit, Offset}), CountUnread, MarkRead, MarkAllRead. Every read/update takes recipientID explicitly, so a handler built on top can never touch another recipient's rows by accident. Building the actual HTTP inbox (response DTOs, the rbac.Identity.Subject → recipientID guard) is your module's own territory: the kit stops at the Store, the same boundary the outbox draws around its own table.
NewChannel(store) wraps a Store into the notify.Channel the Hub dispatches to; it is what a Databasable notification's content routes through.
There is no NOTIFY_* env config: channel enablement is the wiring; the Hub contains exactly the channels the entry point constructs.
MemoryStore
store := notifydatabase.NewMemoryStore() // implements the full Store, including the read side
MemoryStore is a test double: it implements every Store method, so a product can test its inbox handlers (list, mark read, count unread) without Postgres. Pair it with notifydatabase.NewChannel(store) for content-rendering coverage in a Hub, the same way mail.NewMemory() stands in for a real mailer.
Migration
The notifications table arrives as a goose migration, the same pattern outbox.MigrationSQL uses: notifydatabase.MigrationSQL is the schema's source of truth in the package, and add notification's first run writes it into the project's migrations/ directory at the next available timestamp. Run it before starting the service that owns the inbox: go run ./cmd/migrate up (see Migrations).