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

  1. Use placeholders$1, $2 nunca concatene strings
  2. Close tudorows.Close(), stmt.Close()
  3. Configure o pool — Evita conexões excessivas
  4. Use context — Para timeouts e cancelamento
  5. Migrations — Versionamento do schema
  6. 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