Go Web App Walkthrough

As a follow up to my previous post, I thought I would walk through the basic web app I built using Go, Gin, and GORM. I realize this might not be the most idiomatic Go program, but I was going for simplicity and brevity.

You can find this code in the file main.go in my goweb GitHub repo.

The Code

Every Go application starts with a package definition. Since this program is an executable and not a library, call this package main. This tells the compiler that the main function, where execution starts, is found here.

package main

Next, import libraries needed by the program. In this case gin and gorm, as mentioned earlier, as well as the GORM postgres driver. The program needs godotenv and os for loading environment variables and finally net/http which defines HTTP status codes.

import (
    "github.com/gin-gonic/gin"
    "github.com/jinzhu/gorm"
    _ "github.com/jinzhu/gorm/dialects/postgres"
    "github.com/joho/godotenv"
    "net/http"
    "os"
)

Next, define the User model. This is a standard Go struct that includes gorm.Model. This adds ID and time stamp fields.

type User struct {
    gorm.Model
    Name string
}

The global variable db is used throughout the program to send queries to the database.

var db *gorm.DB

The usersIndex function is a request handler. It finds an array of users in the database and uses the Gin context to render them as HTML using the index.html template.

func usersIndex(c *gin.Context) {
    var users []User

    db.Find(&users)

    c.HTML(http.StatusOK, "index.html", gin.H{
        "users": users,
    })
}

Finally, the main function, where execution begins.

func main() {

First, load the contents of the .env file. If there’s an error, panic. That is, end the program with an error message.

    err := godotenv.Load()
    if err != nil {
        panic("Error loading .env file")
    }

Now that the environment is loaded, use the DATABASE_URL variable to connect to the database. Again, panic if there is an error. The defer statement calls dB.Close() when this function ends to close the database connection.

    db, err = gorm.Open("postgres", os.Getenv("DATABASE_URL"))
    if err != nil {
        panic(err)
    }
    defer db.Close()

Use the connection to auto migrate the database. This creates the users table, based on the plural version of the model name, with columns named id, created_at, updated_at, deleted_at, and name.

    db.AutoMigrate(&User{})

Next, count the number of users in the database. If there are zero users, create a few examples. You might think of this as seed data.

    count := 0
    db.Table("users").Count(&count)

    if count == 0 {
        db.Create(&User{Name: "Alice"})
        db.Create(&User{Name: "Bob"})
        db.Create(&User{Name: "Carol"})
    }

Create a Gin router using gin.Default().

    router := gin.Default()

Tell the router where to find static files, such as CSS, JavaScript, and images. Also, load the HTML files in the templates directory.

    router.Static("/assets", "./assets")
    router.LoadHTMLGlob("templates/*")

Next, tell the router to handle GET requests to the root path by calling the usersIndex function defined earlier.

    router.GET("/", usersIndex)

Finally, call router.Run() to start handling requests in a loop.

    router.Run()
}

And with that you have a complete program that uses data from a database to render HTML templates in response to web requests.