Connect to PostgreSQL with Golang

This article was published on

The goal is simple: create a secure PostgreSQL connection in Golang that will allow you to continue with your mission.

Required Packages

The two packages we're going to need for this are:

  • github.com/lib/pq - Postgres driver for the database/sql package
  • github.com/joho/godotenv - Used to load environment variables from .env files

Installing Dependencies

Let's go ahead and install the dependencies and dive straight into the code.

go get github.com/lib/pq
go get github.com/joho/godotenv

Obtaining Database Credentials

Obtain your PostgreSQL database connection credentials. There are many cloud providers you can look into, such as Railway, or you could opt for a local PostgreSQL instance if you prefer.

Creating the .env File

We'll need to create a .env file at the root of our project. This file is where we can store sensitive information about the project. It typically includes sensitive information such as API Keys and database credentials.

Note: This file should be excluded from version control software and kept locally on the machine where the software is running.

PG_USER=# Your PostgreSQL Username
PG_DBNAME=# Your PostgreSQL Database Instance
PG_PASSWORD=# Your PostgreSQL Password
PG_HOST=# Your PostgreSQL Host
PG_PORT=# Your PostgreSQL Post
PG_SSLMODE=disable

Building the Connection String

Now, we can load the environment variables and build the PostgreSQL connection string. I prefer to keep this logic in an isolated file because I'll likely need it in various places.

Here's the foundation for an /utils/database.go file.

package utils

import (
	"github.com/joho/godotenv"

	"fmt"
	"os"
)

func buildConnectionString() (string, error) {
	// Load the .env file 
	err := godotenv.Load()
	if err != nil {
		return "", err
	}

	PG_USER := os.Getenv("PG_USER")
	PG_DBNAME := os.Getenv("PG_DBNAME")
	PG_PASSWORD := os.Getenv("PG_PASSWORD")
	PG_HOST := os.Getenv("PG_HOST")
	PG_PORT := os.Getenv("PG_PORT")
	PG_SSLMODE := os.Getenv("PG_SSLMODE")

	connStr := fmt.Sprintf("user=%s dbname=%s password=%s host=%s port=%s sslmode=%s", PG_USER, PG_DBNAME, PG_PASSWORD, PG_HOST, PG_PORT, PG_SSLMODE)

	return connStr, nil
}

We went into depth on how to use environment variables in Go if you're curious and want a more fundamental understanding.

Establishing the Database Connection

Now, we're good to establish the database connection. Import the "database/sql" "github.com/lib/pq" packages. The PostgreSQL driver we installed can be referenced as a blank import because we need to ensure the driver exists for the database/sql package.

Building on top of what we've started above:

package utils

import (
	"database/sql"

	"github.com/joho/godotenv"
	_ "github.com/lib/pq"

	"fmt"
	"os"
)

func buildConnectionString() (string, error) {
  // ... Details omitted
}

// Database opens a new database connection
func Database() (*sql.DB, error) {
	// Build the connection string
	connStr, err := buildConnectionString()
	if err != nil {
		return nil, err
	}

	// Open a database connection
	db, err := sql.Open("postgres", connStr)
	if err != nil {
		return nil, err
	}

	// Test the connection
	err = db.Ping()
	if err != nil {
		// Close the database connection if Ping fails
		db.Close()
		return nil, err
	}

	return db, nil
}

Testing the Connection

That's not so bad. Now we can test the connection from the main.go

package main

import (
	"log"
	"net/http"

	"your-workspace/utils"
	_ "github.com/lib/pq"
)

func main() {
	db, err := utils.Database()
	if err != nil {
		log.Fatalf("Failed to connect to database: %v", err)
	}
	// Ensure the database connection is closed when the main function exits
	defer db.Close() 
	

	// Test the connection
	err = db.Ping()
	if err != nil {
		log.Fatalf("Failed to ping database: %v", err)
	} else {
		log.Println("Successfully connected and pinged the database.")
	}

	// Continue with the rest of your application...
}

All we need to do from here is confirm that the database connection has been established.

go run main.go

# 2024/03/12 20:24:34 Successfully connected and pinged the database.

And with that, we've successfully established a connection to the PostgreSQL database. Cheers.