Create a server with PostgreSQL in Go - Part[1/3] of Go Authentication series
Tags: go, security, beginners
Published: 2020-10-31 06:51:36

In this tutorial, you will learn how to make an authentication boilerplate with [Go](https://golang.org/doc/). We will use [GoFiber](https://gofiber.io/) and [PostgreSQL](https://www.postgresql.org/docs/). We will go from the ground up to build this project.

# Getting Started

You need to have at least go 1.11 installed on your system we will use go mod to support versioning.

### 1. Create a Simple Server
Let us first create a simple Go API to start with. We will use Fiber which is an Express-inspired web framework written in Go.

So first of all, create a new directory called `go-authentication-boilerplate` and then `cd` into it.
After that run `go mod init` to build the go modules inside our app. More information on go modules can be found [here](https://blog.golang.org/using-go-modules).

Now, create a file named: _main.go_ inside our home directory.
```go
package main

import (
	"log"

	"github.com/gofiber/fiber/v2"
	"github.com/gofiber/fiber/v2/middleware/cors"
)

// CreateServer creates a new Fiber instance
func CreateServer() *fiber.App {
	app := fiber.New()

	return app
}


func main() {
	app := CreateServer()

	app.Use(cors.New())

    app.Get("/hello", func(c *fiber.Ctx) {
            return c.SendString("Hello, World!")
    }

	// 404 Handler
	app.Use(func(c *fiber.Ctx) error {
		return c.SendStatus(404) // => 404 "Not Found"
	})
 
	log.Fatal(app.Listen(":3000"))
}
```

Now, run this server with the command `go run main.go` and go to `http://localhost:3000/hello`. You will see a _Hello, World!_ text there.

### 2. Connect PostgreSQL with the server.
First of all, we need to create a database. Let us name our database **go-auth**. We can create a database either with bash/zsh or with psql by running the following commands:
```bash
createdb go-auth # in bash/zsh

CREATE DATABASE go-auth; # in psql 
```

We are going to use [Gorm](https://gorm.io/index.html) to connect our server with our database. Gorm is an ORM library for Golang.

Now we will store some environment variables inside a _.env_. A sample _.env_ file for this project would look like:
```env
PSQL_USER=postgres
PSQL_PASS=postgres
PSQL_DBNAME=go-auth
PSQL_PORT=5432
```

Now, we create a new file called postgres.go inside a new directory called database. Here, we will create our database session and export it from this package for other packages to use. We are also going to use [godotenv](https://github.com/joho/godotenv) to use the environment variables inside this file. Then we will call this function from our  _**main**_ function to instantiate the database.

**database/postgres.go**
```go
package database

import (
	"fmt"
	"go-authentication-boilerplate/models"
	"log"
	"os"

	"github.com/joho/godotenv"
	"gorm.io/driver/postgres"
	"gorm.io/gorm"
	"gorm.io/gorm/logger"
)

// DB represents a Database instance
var DB *gorm.DB

// ConnectToDB connects the server with database
func ConnectToDB() {
	err := godotenv.Load()
	if err != nil {
		log.Fatal("Error loading env file \n", err)
	}

	dsn := fmt.Sprintf("host=localhost user=%s password=%s dbname=%s port=%s sslmode=disable TimeZone=Asia/Kolkata",
		os.Getenv("PSQL_USER"), os.Getenv("PSQL_PASS"), os.Getenv("PSQL_DBNAME"), os.Getenv("PSQL_PORT"))

	log.Print("Connecting to PostgreSQL DB...")
	DB, err = gorm.Open(postgres.Open(dsn), &gorm.Config{
		Logger: logger.Default.LogMode(logger.Info),
	})
	
	if err != nil {
		log.Fatal("Failed to connect to database. \n", err)
		os.Exit(2)

	}
	log.Println("connected")
}
```

Now that we are done with connecting our database to our server, let's start with database models.

### 3. Creating the models

Now go ahead and create a new folder named _models_. Inside that folder create a new file called _models.go_. This file will contain the base struct that will contain common columns for all other models.

**models/models.go**
```go
package models

import (
	"time"

	"github.com/google/uuid"
	"gorm.io/gorm"
)

// GenerateISOString generates a time string equivalent to Date.now().toISOString in JavaScript
func GenerateISOString() string {
	return time.Now().UTC().Format("2006-01-02T15:04:05.999Z07:00")
}

// Base contains common columns for all tables
type Base struct {
	ID        uint      `gorm:"primaryKey"`
	UUID      uuid.UUID `json:"_id" gorm:"primaryKey;autoIncrement:false"`
	CreatedAt string    `json:"created_at"`
	UpdatedAt string    `json:"updated_at"`
}

// BeforeCreate will set Base struct before every insert
func (base *Base) BeforeCreate(tx *gorm.DB) error {
	// uuid.New() creates a new random UUID or panics.
	base.UUID = uuid.New()

	// generate timestamps
	t := GenerateISOString()
	base.CreatedAt, base.UpdatedAt = t, t

	return nil
}

// AfterUpdate will update the Base struct after every update
func (base *Base) AfterUpdate(tx *gorm.DB) error {
	// update timestamps
	base.UpdatedAt = GenerateISOString()
	return nil
}
```

Now let us create a new file inside _models_ folder called _user.go_. This file will contain all the models which are relevant to user routes.

**models/user.go**:
```go
package models

// User represents a User schema
type User struct {
	Base
	Email    string `json:"email" gorm:"unique"`
	Username string `json:"username" gorm:"unique"`
	Password string `json:"password"`
}
```

Now we will modify our **main.go** file to connect our database with the server.

**main.go**
```go
package main

import (
	"log"

++	"go-authentication-boilerplate/database"

	"github.com/gofiber/fiber/v2"
	"github.com/gofiber/fiber/v2/middleware/cors"
)

// CreateServer creates a new Fiber instance
func CreateServer() *fiber.App {
	app := fiber.New()

	return app
}

func main() {
++	// Connect to Postgres
++	database.ConnectToDB()
	app := CreateServer()

	app.Use(cors.New())

	// 404 Handler
	app.Use(func(c *fiber.Ctx) error {
		return c.SendStatus(404) // => 404 "Not Found"
	})

	log.Fatal(app.Listen(":3000"))
}
```
<sub><sup>NOTE: ++ represents the added or modified lines</sup></sub>

### 4. Setup Routes
Now we need to set up the needed routes for our application. We will start by making a new folder called _router_. Inside that folder, create a new file called _setup.go_. Here we will setup all our routes.

**router/setup.go**
```go
package router

import (
	"github.com/gofiber/fiber/v2"
)

func hello(c *fiber.Ctx) error {
	return c.SendString("Hello World!")
}

// SetupRoutes setups all the Routes
func SetupRoutes(app *fiber.App) {
	api := app.Group("/api")

	api.Get("/", hello)
}
```

Now we will call this SetupRoutes function inside our _main.go_ file.

**main.go**
```go
package main

import (
	"log"

	"go-authentication-boilerplate/database"
++	"go-authentication-boilerplate/router"

	"github.com/gofiber/fiber/v2"
	"github.com/gofiber/fiber/v2/middleware/cors"
)

// CreateServer creates a new Fiber instance
func CreateServer() *fiber.App {
	app := fiber.New()

	return app
}

func main() {
	// Connect to Postgres
	database.ConnectToDB()
	app := CreateServer()

	app.Use(cors.New())

++	router.SetupRoutes(app)

	// 404 Handler
	app.Use(func(c *fiber.Ctx) error {
		return c.SendStatus(404) // => 404 "Not Found"
	})

	log.Fatal(app.Listen(":3000"))
}
```
<sub><sup>NOTE: ++ represents the added or modified lines</sup></sub>

Now that we have scaffolded our Server, we will now create SignUp and SignIn route. We will do this in the next part of this tutorial.

_Thanks for reading! If you liked this article, please let me know and share it!_