¿Por qué Go y PostgreSQL son la pareja ideal?
\n\nEn el mundo del desarrollo backend, Go (o Golang) y PostgreSQL se han consolidado como un potente tándem para crear sistemas de alta carga y fiables. Go nos brinda un rendimiento increíble, simplicidad y un excelente soporte para la concurrencia, mientras que PostgreSQL es una base de datos de nivel industrial con amplia funcionalidad, fiabilidad y extensibilidad. Al combinarlos, obtiene un stack capaz de soportar las cargas de una startup y los requisitos de un proyecto empresarial.
\n\nEn este artículo no solo repasaremos la sintaxis. Crearemos una guía práctica completa: desde la instalación del driver hasta la optimización del pool de conexiones y el trabajo con transacciones. Aprenderá a evitar errores típicos y a escribir código eficiente en Go para trabajar con PostgreSQL. ¡Allá vamos!
\n\nPrimeros pasos: elegir el driver y conectarse
\n\nLo primero que hay que hacer es instalar el driver. En el mundo de Go hay dos candidatos principales:
\n- \n
- lib/pq — clásico, simple, pero ya no se mantiene activamente. \n
- pgx — moderno, rápido, en desarrollo activo, soporta más funcionalidades de PostgreSQL (por ejemplo, COPY, LISTEN/NOTIFY, arrays). Lo recomiendo. \n
Instalamos pgx:
\ngo get github.com/jackc/pgx/v5\n\nAhora escribamos el código para conectarnos a la base de datos. Crearemos un archivo 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 conexión: usuario, contraseña, host, puerto, nombre BD\n dsn := "postgres://myuser:mypassword@localhost:5432/mydb?sslmode=disable"\n\n // Creamos un pool de conexiones\n pool, err := pgxpool.New(context.Background(), dsn)\n if err != nil {\n log.Fatalf("No se pudo crear el pool: %v", err)\n }\n defer pool.Close()\n\n // Verificamos la conexión\n err = pool.Ping(context.Background())\n if err != nil {\n log.Fatalf("No se pudo conectar: %v", err)\n }\n\n fmt.Println("¡Conectado a PostgreSQL exitosamente!")\n}\n\nNota: estamos usando pgxpool, no una conexión simple. El pool de conexiones es imprescindible para producción. Permite reutilizar conexiones, evitando la sobrecarga de crearlas y cerrarlas.
Operaciones CRUD: crear, leer, actualizar y eliminar
\n\nAhora implementemos las operaciones clásicas con la tabla users. Supongamos que tenemos la estructura:
type User struct {\n ID int\n Name string\n Age int\n}\n\n\nCreación de tabla (Create)
\nEs mejor ejecutar las migraciones con herramientas separadas, pero para el ejemplo mostraré cómo ejecutar SQL directo:
\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\nInserción de registro (Insert)
\nUsamos QueryRow para devolver el ID generado:
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\nLectura 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