What it is
pgx is a PostgreSQL driver and toolkit for Go that bypasses database/sql to expose PostgreSQL-specific features and significantly better performance.
The native pgx API supports connection pooling, COPY, LISTEN/NOTIFY, native PostgreSQL types such as arrays and JSONB, and binary protocol transfers. Use pgxpool for concurrent applications.
Installation
go get github.com/jackc/pgx/v5Getting started
The smallest useful thing you can do with it, and what each part means.
pool, err := pgxpool.New(ctx, os.Getenv("DATABASE_URL"))
if err != nil { return err }
defer pool.Close()
rows, err := pool.Query(ctx, "SELECT id, title FROM books WHERE year > $1", 1990)
if err != nil { return err }
// CollectRows removes the manual rows.Next loop.
books, err := pgx.CollectRows(rows, pgx.RowToStructByName[Book])rowsAffected, err := pool.CopyFrom(ctx,
pgx.Identifier{"books"},
[]string{"title", "year"},
pgx.CopyFromSlice(len(books), func(i int) ([]any, error) {
return []any{books[i].Title, books[i].Year}, nil
}),
)Advanced usage
Where the library earns its place over a simpler alternative.
tx, err := pool.Begin(ctx)
if err != nil { return err }
defer tx.Rollback(ctx) // no-op once Commit succeeds
metadata := map[string]any{"tags": []string{"scifi"}, "rating": 4.5}
_, err = tx.Exec(ctx,
"INSERT INTO books (title, metadata) VALUES ($1, $2)",
"Dune", metadata) // encoded to JSONB natively
if err != nil { return err }
return tx.Commit(ctx)Errors and fixes
The failures you are most likely to hit, and what actually resolves them.
- no rows in result set
- QueryRow returns pgx.ErrNoRows. Check with errors.Is(err, pgx.ErrNoRows) — note this is pgx's own error, not sql.ErrNoRows.
- Constraint violations are hard to distinguish
- Cast to *pgconn.PgError and inspect .Code — 23505 is unique_violation, 23503 is foreign_key_violation.
Best practices
- Use pgxpool rather than a single connection for anything concurrent.
- Prefer the native pgx API over the database/sql adapter unless you need driver portability.
- Use CopyFrom for bulk inserts instead of looping Exec.
- Always pass a context; pgx uses it for both cancellation and statement timeouts.
Background
Why it exists, and what it was reacting to.
Built by Jack Christensen, pgx exists because database/sql's lowest-common-denominator interface hides much of what PostgreSQL can do. It offers both a native API and a database/sql-compatible adapter.
