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.