Por que Go e PostgreSQL são a dupla ideal?
\n\nNo mundo do desenvolvimento backend, Go (ou Golang) e PostgreSQL se destacam como uma poderosa combinação para criar sistemas de alta carga e confiáveis. Go nos oferece desempenho incrível, simplicidade e excelente suporte à concorrência, enquanto o PostgreSQL é um banco de dados de nível industrial com funcionalidades ricas, confiabilidade e extensibilidade. Ao combiná-los, você obtém uma stack capaz de suportar as cargas de uma startup e os requisitos de projetos enterprise.
\n\nNeste artigo, não vamos apenas passar pela sintaxe. Vamos criar um guia prático completo: desde a instalação do driver até a otimização do pool de conexões e o trabalho com transações. Você aprenderá como evitar erros comuns e escrever código eficiente em Go para trabalhar com PostgreSQL. Vamos lá!
\n\nPrimeiros passos: escolhendo o driver e conectando
\n\nA primeira coisa a fazer é instalar o driver. No mundo Go, existem dois principais candidatos:
\n- \n
- lib/pq — clássico, simples, mas não é mais ativamente mantido. \n
- pgx — moderno, rápido, ativamente desenvolvido, suporta mais funcionalidades do PostgreSQL (por exemplo, COPY, LISTEN/NOTIFY, arrays). Recomendo este. \n
Instalando o pgx:
\ngo get github.com/jackc/pgx/v5\n\nAgora vamos escrever o código para conectar ao banco de dados. Vamos criar o arquivo main.go:
package main\n\nimport (\n "context"\n "fmt"\n "log"\n\n "github.com/jackc/pgx/v5/pgxpool"\n)\n\nfunc main() {\n // URL de conexão: usuário, senha, host, porta, nome do BD\n dsn := "postgres://myuser:mypassword@localhost:5432/mydb?sslmode=disable"\n\n // Criando um pool de conexões\n pool, err := pgxpool.New(context.Background(), dsn)\n if err != nil {\n log.Fatalf("Falha ao criar pool: %v", err)\n }\n defer pool.Close()\n\n // Verificando a conexão\n err = pool.Ping(context.Background())\n if err != nil {\n log.Fatalf("Falha ao conectar: %v", err)\n }\n\n fmt.Println("Conectado ao PostgreSQL com sucesso!")\n}\n\nObserve: estamos usando pgxpool, não uma conexão simples. Pool de conexões é indispensável para produção. Ele permite reutilizar conexões, evitando a sobrecarga de criá-las e fechá-las.
Operações CRUD: criar, ler, atualizar e deletar
\n\nAgora vamos implementar as operações clássicas com a tabela users. Suponha que temos a estrutura:
type User struct {\n ID int\n Name string\n Age int\n}\n\n\nCriação da tabela (Create)
\nÉ melhor executar migrações com ferramentas separadas, mas para exemplo, mostrarei como executar SQL puro:
\nfunc createTable(ctx context.Context, pool *pgxpool.Pool) error {\n query := `\n CREATE TABLE IF NOT EXISTS users (\n id SERIAL PRIMARY KEY,\n name TEXT NOT NULL,\n age INT NOT NULL\n );\n `\n _, err := pool.Exec(ctx, query)\n return err\n}\n\n\nInserção de registro (Insert)
\nUsamos QueryRow para retornar o ID gerado:
func createUser(ctx context.Context, pool *pgxpool.Pool, user *User) error {\n query := `INSERT INTO users (name, age) VALUES ($1, $2) RETURNING id`\n err := pool.QueryRow(ctx, query, user.Name, user.Age).Scan(&user.ID)\n return err\n}\n\n\nLeitura de registro (Select)
\nfunc getUserByID(ctx context.Context, pool *pgxpool.Pool, id int) (*User, error) {\n query := `SELECT id, name, age FROM users WHERE id = $1`\n user := &User{}\n err := pool.QueryRow(ctx, query, id).Scan(&user.ID, &user.Name, &user.Age)\n if err != nil {\n return nil, err\n }\n return user, nil\n}\n\n\n