Video:

Implementing Our Repository Layer

February 13, 2021

Course Instructor: Elliot Forbes

Hey Gophers! My name is Elliot and I'm the creator of TutorialEdge and I've been working with Go systems for roughly 5 years now.

Twitter: @Elliot_f

Now that our Postgres DB is up and running, we now have to implement the package that we’ll use to communicate to this database.

Create a new directory within your internal directory called database. Within that create a new file called database.go:

package database

import (
	"fmt"
	"os"

	"github.com/jinzhu/gorm"
	_ "github.com/jinzhu/gorm/dialects/postgres"
)

// NewDatabase - returns a pointer to a new database connection
func NewDatabase() (*gorm.DB, error) {
	return nil, nil
}

Now, within the NewDatabase function, we’ll want to setup a connection and then ping that connection to ensure it’s working as intended.

In order to get the configuration, we never want to hardcode anything so we’ll leverage the os package and pick all of the connection information from environment variables.

Once we’ve got those environment variables set up, let’s use the GORM package with the postgres driver in order to create a pointer to a DB connection.

Once we’ve got this, we can then ping the database and this should tell us whether or not we’ve been successful in connecting.

package database

import (
	"fmt"
	"os"

	"github.com/jinzhu/gorm"
	_ "github.com/jinzhu/gorm/dialects/postgres"
)

// NewDatabase - returns a pointer to a new database connection
func NewDatabase() (*gorm.DB, error) {
	dbUsername := os.Getenv("DB_USERNAME")
	dbPassword := os.Getenv("DB_PASSWORD")
	dbHost := os.Getenv("DB_HOST")
	dbDatabaseName := os.Getenv("DB_DATABASE_NAME")
	dbPort := os.Getenv("DB_PORT")

	// dbConnectionString := dbUsername + ":" + dbPassword + "@tcp(" + dbHost + ":" + strconv.Itoa(dbPort) + ")/" + dbDatabaseName
	connectionString := fmt.Sprintf("host=%s port=%s user=%s dbname=%s password=%s sslmode=disable", dbHost, dbPort, dbUsername, dbDatabaseName, dbPassword)
	fmt.Println(connectionString)

	db, err := gorm.Open("postgres", connectionString)
	if err != nil {
		return db, err
	}

	if err := db.DB().Ping(); err != nil {
		return db, err
	}

	return db, nil
}

With this in place, let’s jump back into the cmd/server/main.go and call this newly defined NewDatabase function:

// App - the struct which contains things like pointers
// to database connections
type App struct{}

// Run - sets up our application
func (app *App) Run() error {
	fmt.Println("Setting Up Our APP")

	var err error
	db, err := database.NewDatabase()
	if err != nil {
		return err
	}
	
	handler := transportHTTP.NewHandler(commentService)
	handler.SetupRoutes()

	if err := http.ListenAndServe(":8080", handler.Router); err != nil {
		fmt.Println("Failed to set up server")
		return err
	}

	return nil
}

With this in place, let’s jump down into the terminal and run the following:

$ export DB_USERNAME=postgres
$ export DB_PASSWORD=postgres
$ export DB_TABLE=postgres
$ export DB_PORT=5432
$ export DB_DB=postgres
$ go run cmd/server/main.go

You should see that the application picks up these underlying environment variables and is able to connect to our running Postgres database without throwing any errors!

Erratum

  • The DB_TABLE should be replaced with DB_DATABASE_NAME and the variable you use in the constructor should also be changed to dbDatabaseName.

Since releasing this course, gorm have made some changes to how a connection is established. This code below, provided very kindly by Dmitry, should reflect how you can establish a connection with the latest version of their package.

connectionString := fmt.Sprintf("host=%s port=%s user=%s dbname=%s password=%s", dbHost, dbPort, dbUsername, dbDatabaseName, dbPassword)

db, err := gorm.Open(postgres.Open(connectionString), &gorm.Config{})
if err != nil {
	return db, err
}

postgresDb, err := db.DB()
if err != nil {
	return db, err
}
if err := postgresDb.Ping(); err != nil {
	return db, err
}