Introduction: Why Go and Databases Are the Perfect Pair
The Go programming language (Golang) has gained popularity due to its high performance, built-in concurrency support, and ease of deployment. However, the true power of Go is revealed when working with databases. Whether it's relational PostgreSQL or document-oriented MongoDB, Go provides elegant tools for efficient data interaction.
In this article, we will cover the practical aspects of connecting, performing CRUD operations, and optimizing queries for PostgreSQL and MongoDB in Go. You will learn how to use standard packages and popular drivers to write reliable and fast code.
1. Working with PostgreSQL in Go
To work with PostgreSQL in Go, the standard database/sql package is used in conjunction with the pgx or lib/pq driver. pgx is recommended due to its better performance and support for advanced PostgreSQL features.
1.1. Installation and Connection
Install the driver via go modules:
go get github.com/jackc/pgx/v5Example of connecting to a database:
package main
import ( "context" "fmt" "github.com/jackc/pgx/v5/pgxpool" "log")
func main() { dsn := "postgres://user:password@localhost:5432/mydb?sslmode=disable" pool, err := pgxpool.New(context.Background(), dsn) if err != nil { log.Fatal("Unable to connect to database:", err) } defer pool.Close()
fmt.Println("Connected to PostgreSQL!")}Tip: Use a connection pool (pgxpool) for production environments — this reduces the overhead of establishing new connections.
1.2. CRUD Operations
Let's look at basic operations using the users table as an example:
// CREATEfunc createUser(pool *pgxpool.Pool, name string, email string) (int, error) { var id int err := pool.QueryRow(context.Background(), "INSERT INTO users (name, email) VALUES ($1, $2) RETURNING id", name, email).Scan(&id) if err != nil { return 0, err } return id, nil}
// READfunc getUser(pool *pgxpool.Pool, id int) (string, string, error) { var name, email string err := pool.QueryRow(context.Background(), "SELECT name, email FROM users WHERE id = $1", id).Scan(&name, &email) if err != nil { return "", "", err } return name, email, nil}
// UPDATEfunc updateUserEmail(pool *pgxpool.Pool, id int, newEmail string) error { _, err := pool.Exec(context.Background(), "UPDATE users SET email = $1 WHERE id = $2", newEmail, id) return err}
// DELETEfunc deleteUser(pool *pgxpool.Pool, id int) error { _, err := pool.Exec(context.Background(), "DELETE FROM users WHERE id = $1", id) return err}Note the use of parameterized queries with $1, $2 — this protects against SQL injection.
1.3. Transactions
PostgreSQL supports transactions, which are critical for data integrity:
func transferMoney(pool *pgxpool.Pool, fromID, toID int, amount float64) error { tx, err := pool.Begin(context.Background()) if err != nil { return err } defer tx.Rollback(context.Background()) // rollback on error
_, err = tx.Exec(context.Background(), "UPDATE accounts SET balance = balance - $1 WHERE id = $2", amount, fromID) if err != nil { return err }
_, err = tx.Exec(context.Background(), "UPDATE accounts SET balance = balance + $1 WHERE id = $2", amount, toID) if err != nil { return err }
return tx.Commit(context.Background())}2. Working with MongoDB in Go
MongoDB is a document