Create a server with PostgreSQL in Go - Part[1/3] of Go Authentication series
Faizan
Posted on October 31, 2020
In this tutorial, you will learn how to make an authentication boilerplate with Go. We will use GoFiber and PostgreSQL. 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.
Now, create a file named: main.go inside our home directory.
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:
createdb go-auth # in bash/zsh
CREATE DATABASE go-auth; # in psql
We are going to use Gorm 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:
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 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
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
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:
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
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"))
}
NOTE: ++ represents the added or modified lines
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
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
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"))
}
NOTE: ++ represents the added or modified lines
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!
Posted on October 31, 2020
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.