Go com PostgreSQL: Tutorial Completo
PostgreSQL é o banco mais usado com Go. Aprenda a integrar os dois de forma profissional.
Setup
Instalar driver
go get github.com/lib/pq
# ou o mais moderno pgx
go get github.com/jackc/pgx/v5
Docker para desenvolvimento
docker run --name postgres \
-e POSTGRES_PASSWORD=senha123 \
-p 5432:5432 \
-d postgres:15
Conexão Básica
package main
import (
"database/sql"
"log"
_ "github.com/lib/pq"
)
func main() {
connStr := "host=localhost port=5432 user=postgres password=senha123 dbname=myapp sslmode=disable"
db, err := sql.Open("postgres", connStr)
if err != nil {
log.Fatal(err)
}
defer db.Close()
// Testar conexão
if err := db.Ping(); err != nil {
log.Fatal(err)
}
log.Println("Conectado ao PostgreSQL!")
}
Criar Tabela
func createTable(db *sql.DB) error {
query := `
CREATE TABLE IF NOT EXISTS users (
id SERIAL PRIMARY KEY,
name VARCHAR(100) NOT NULL,
email VARCHAR(100) UNIQUE NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)`
_, err := db.Exec(query)
return err
}
CRUD Completo
Modelo
type User struct {
ID int `json:"id"`
Name string `json:"name"`
Email string `json:"email"`
CreatedAt time.Time `json:"created_at"`
}
Create
func createUser(db *sql.DB, user *User) error {
query := `
INSERT INTO users (name, email)
VALUES ($1, $2)
RETURNING id, created_at`
return db.QueryRow(query, user.Name, user.Email).
Scan(&user.ID, &user.CreatedAt)
}
Read
// Buscar um
func getUserByID(db *sql.DB, id int) (*User, error) {
user := &User{}
query := `SELECT id, name, email, created_at FROM users WHERE id = $1`
err := db.QueryRow(query, id).
Scan(&user.ID, &user.Name, &user.Email, &user.CreatedAt)
if err == sql.ErrNoRows {
return nil, nil
}
return user, err
}
// Listar todos
func listUsers(db *sql.DB) ([]User, error) {
query := `SELECT id, name, email, created_at FROM users ORDER BY id`
rows, err := db.Query(query)
if err != nil {
return nil, err
}
defer rows.Close()
var users []User
for rows.Next() {
var u User
if err := rows.Scan(&u.ID, &u.Name, &u.Email, &u.CreatedAt); err != nil {
return nil, err
}
users = append(users, u)
}
return users, rows.Err()
}
Update
func updateUser(db *sql.DB, id int, name, email string) error {
query := `UPDATE users SET name = $1, email = $2 WHERE id = $3`
result, err := db.Exec(query, name, email, id)
if err != nil {
return err
}
rowsAffected, _ := result.RowsAffected()
if rowsAffected == 0 {
return fmt.Errorf("user not found")
}
return nil
}
Delete
func deleteUser(db *sql.DB, id int) error {
query := `DELETE FROM users WHERE id = $1`
result, err := db.Exec(query, id)
if err != nil {
return err
}
rowsAffected, _ := result.RowsAffected()
if rowsAffected == 0 {
return fmt.Errorf("user not found")
}
return nil
}
Transações
func transferMoney(db *sql.DB, fromID, toID int, amount float64) error {
tx, err := db.Begin()
if err != nil {
return err
}
defer tx.Rollback() // Rollback se não fizer Commit
// Debitar
_, err = tx.Exec(
`UPDATE accounts SET balance = balance - $1 WHERE id = $2`,
amount, fromID,
)
if err != nil {
return err
}
// Creditar
_, err = tx.Exec(
`UPDATE accounts SET balance = balance + $1 WHERE id = $2`,
amount, toID,
)
if err != nil {
return err
}
return tx.Commit()
}
Connection Pooling
func setupDB() *sql.DB {
db, err := sql.Open("postgres", connStr)
if err != nil {
log.Fatal(err)
}
// Configurar pool
db.SetMaxOpenConns(25) // Máximo de conexões abertas
db.SetMaxIdleConns(5) // Conexões ociosas mantidas
db.SetConnMaxLifetime(5 * time.Minute) // Tempo máximo de vida
return db
}
Migrations com golang-migrate
go install -tags 'postgres' github.com/golang-migrate/migrate/v4/cmd/migrate@latest
# Criar migration
migrate create -ext sql -dir migrations -seq create_users_table
# migrations/000001_create_users_table.up.sql
CREATE TABLE users (
id SERIAL PRIMARY KEY,
name VARCHAR(100) NOT NULL
);
# migrations/000001_create_users_table.down.sql
DROP TABLE users;
# Executar
migrate -path migrations -database "postgres://..." up
Usando pgx (Recomendado)
pgx é mais rápido e tem mais features:
import (
"context"
"github.com/jackc/pgx/v5/pgxpool"
)
func main() {
ctx := context.Background()
pool, err := pgxpool.New(ctx, "postgres://user:pass@localhost/db")
if err != nil {
log.Fatal(err)
}
defer pool.Close()
// Query
var name string
err = pool.QueryRow(ctx, "SELECT name FROM users WHERE id = $1", 1).
Scan(&name)
}
Batch Queries
batch := &pgx.Batch{}
batch.Queue("INSERT INTO users (name) VALUES ($1)", "Alice")
batch.Queue("INSERT INTO users (name) VALUES ($1)", "Bob")
batch.Queue("INSERT INTO users (name) VALUES ($1)", "Carol")
results := pool.SendBatch(ctx, batch)
defer results.Close()
Prepared Statements
stmt, err := db.Prepare("SELECT * FROM users WHERE id = $1")
if err != nil {
log.Fatal(err)
}
defer stmt.Close()
// Reusar o statement
user1, _ := queryWithStmt(stmt, 1)
user2, _ := queryWithStmt(stmt, 2)
Boas Práticas
- Use placeholders —
$1, $2nunca concatene strings - Close tudo —
rows.Close(),stmt.Close() - Configure o pool — Evita conexões excessivas
- Use context — Para timeouts e cancelamento
- Migrations — Versionamento do schema
- Trate sql.ErrNoRows — Não é erro, é “não encontrado”
Estrutura de Projeto
project/
├── cmd/
│ └── main.go
├── internal/
│ ├── database/
│ │ ├── postgres.go
│ │ └── migrations/
│ └── repository/
│ └── user_repository.go
├── migrations/
│ ├── 000001_create_users.up.sql
│ └── 000001_create_users.down.sql
└── go.mod
Veja Também
Última atualização: Janeiro 2026
Veja também
- API REST com Go — Exponha seus dados via API
- Go com Docker — PostgreSQL no Docker Compose
- Testes em Go — Teste queries e repositórios
- Tratamento de Erros — Handle de erros de banco