The PostgreSQL store uses Grove ORM with the pgdriver for database access. It is the recommended store for production deployments that need ACID transactions and full SQL query power.
Usage01
Standalone
import (
"github.com/xraph/grove"
_ "github.com/xraph/grove/drivers/pgdriver"
pgstore "github.com/xraph/relay/store/postgres"
)
db, err := grove.Open("pg", "postgres://user:pass@localhost:5432/relay?sslmode=disable")
if err != nil {
log.Fatal(err)
}
s := pgstore.New(db)
if err := s.Migrate(ctx); err != nil {
log.Fatal(err)
}
r, err := relay.New(relay.WithStore(s))With Forge extension
When using the Forge extension with Grove, the store is auto-constructed from the DI container:
app.Use(extension.New(
extension.WithGroveDatabase(""), // uses the default grove.DB
))Or via YAML config:
extensions:
relay:
grove_database: ""See the Forge Extension guide for details.
Migrations02
Call s.Migrate(ctx) before first use. Migrations are managed by the Grove migrator with Go-defined migration functions. The migration group is named "relay" and creates five tables:
relay_event_types-- event type catalogrelay_endpoints-- webhook endpointsrelay_events-- inbound eventsrelay_deliveries-- delivery queue withFOR UPDATE SKIP LOCKEDdequeuerelay_dlq-- dead letter queue
Internals03
| Aspect | Detail |
| Driver | Grove ORM with pgdriver |
| Migrations | Grove migrator with Go-defined migrations |
| Dequeue | FOR UPDATE SKIP LOCKED for concurrent delivery polling |
| Transactions | Database-level ACID |
| JSON fields | JSONB for schema, metadata, headers, payload |
| Timestamps | TIMESTAMPTZ |
| Ping | db.Ping(ctx) |
| Close | Closes the Grove connection |
When to use04
Production deployments requiring durability and ACID guarantees.
Multi-process environments with concurrent delivery workers.
When you need
FOR UPDATE SKIP LOCKEDfor efficient dequeue under concurrency.