Relay
1.x
Docs/Relay/PostgreSQL Store
Open

Reading2 min
Updated31 Jul 2026
Sourcev1/stores/postgres.mdx

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 catalog

  • relay_endpoints -- webhook endpoints

  • relay_events -- inbound events

  • relay_deliveries -- delivery queue with FOR UPDATE SKIP LOCKED dequeue

  • relay_dlq -- dead letter queue

Internals03

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 use04

  • 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.