---
title: "Go com PostgreSQL: Tutorial Completo"
url: "https://golang.com.br/aprenda/golang-postgresql/"
markdown_url: "https://golang.com.br/aprenda/golang-postgresql.MD"
description: "Conecte Go ao PostgreSQL: CRUD com pgx, migrations com golang-migrate, connection pooling com pgxpool. Código pronto pra produção com exemplos reais."
date: "2026-01-29"
author: ""
---

# Go com PostgreSQL: Tutorial Completo

Conecte Go ao PostgreSQL: CRUD com pgx, migrations com golang-migrate, connection pooling com pgxpool. Código pronto pra produção com exemplos reais.


# Go com PostgreSQL: Tutorial Completo

PostgreSQL é o banco mais usado com Go. Aprenda a integrar os dois de forma profissional.

## Setup

### Instalar driver

```bash
go get github.com/lib/pq
# ou o mais moderno pgx
go get github.com/jackc/pgx/v5
```

### Docker para desenvolvimento

```bash
docker run --name postgres \
  -e POSTGRES_PASSWORD=senha123 \
  -p 5432:5432 \
  -d postgres:15
```

## Conexão Básica

```go
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

```go
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

```go
type User struct {
    ID        int       `json:"id"`
    Name      string    `json:"name"`
    Email     string    `json:"email"`
    CreatedAt time.Time `json:"created_at"`
}
```

### Create

```go
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

```go
// 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

```go
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

```go
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

```go
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

```go
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

```bash
go install -tags 'postgres' github.com/golang-migrate/migrate/v4/cmd/migrate@latest
```

```bash
# 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:

```go
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

```go
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

```go
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 tudo** — `rows.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

- [API REST com Go](/aprenda/api-rest-go/)
- [Microservices com Go](/aprenda/golang-microservices/)
- [Vagas Go](/vagas/)

---

*Última atualização: Janeiro 2026*

---

## Veja também

- [API REST com Go](/aprenda/api-rest-go/) — Exponha seus dados via API
- [Go com Docker](/aprenda/golang-docker/) — PostgreSQL no Docker Compose
- [Testes em Go](/aprenda/testes-go/) — Teste queries e repositórios
- [Tratamento de Erros](/aprenda/golang-erros/) — Handle de erros de banco
