Getting StartedConnect your framework
Go: pgx
pgx and pgxpool against CapyDB.
Environment
export DATABASE_URL="postgres://user:password@host:6432/db?sslmode=require"
export DATABASE_DIRECT_URL="postgres://user:password@host:5432/db?sslmode=require"pgxpool
package main
import (
"context"
"os"
"github.com/jackc/pgx/v5/pgxpool"
)
func main() {
config, err := pgxpool.ParseConfig(os.Getenv("DATABASE_URL"))
if err != nil {
panic(err)
}
config.MaxConns = 10
pool, err := pgxpool.NewWithConfig(context.Background(), config)
if err != nil {
panic(err)
}
defer pool.Close()
var now string
if err := pool.QueryRow(context.Background(), "SELECT now()::text").Scan(&now); err != nil {
panic(err)
}
}Pooled URL and statement caching
pgx caches prepared statements by default, which breaks behind transaction pooling. On the pooled URL, switch to simple protocol:
config.ConnConfig.DefaultQueryExecMode = pgx.QueryExecModeSimpleProtocolOr add default_query_exec_mode=simple_protocol to the URL. On the direct URL the default cache mode is fine.
Migrations
golang-migrate, goose, atlas, and friends should use DATABASE_DIRECT_URL — they rely on advisory locks and DDL transactions.
Pitfalls
prepared statement "stmtcache_..." already exists— you are using the default exec mode through the pooler; see above.MaxConnsper instance: with multiple replicas, total client connections = replicas × MaxConns. Keep that under the plan budget or stay on the pooled URL.LISTEN/NOTIFYrequires the direct URL.