Relay

PostgreSQL Store

Production-ready PostgreSQL store using Grove ORM.

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.

Usage

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.

Migrations

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 catalog
  • relay_endpoints -- webhook endpoints
  • relay_events -- inbound events
  • relay_deliveries -- delivery queue with FOR UPDATE SKIP LOCKED dequeue
  • relay_dlq -- dead letter queue

Internals

AspectDetail
DriverGrove ORM with pgdriver
MigrationsGrove migrator with Go-defined migrations
DequeueFOR UPDATE SKIP LOCKED for concurrent delivery polling
TransactionsDatabase-level ACID
JSON fieldsJSONB for schema, metadata, headers, payload
TimestampsTIMESTAMPTZ
Pingdb.Ping(ctx)
CloseCloses the Grove connection

When to use

  • Production deployments requiring durability and ACID guarantees.
  • Multi-process environments with concurrent delivery workers.
  • When you need FOR UPDATE SKIP LOCKED for efficient dequeue under concurrency.

On this page