Webapp Boilerplate for Go
Creating a web server with Go is easy, but making it good requires quite
some work - especially logging, error handling, database connection and
static resources are in most cases required, but need a lot of work to
implement every time.
This module replaces your boilerplate code and decreases your time to
get started with the actual work. It's slightly opinionated (i.e. you'll
have to use Gin and
Zerolog, otherwise you're free to use
whatever you want - Pkger is the
recommended way to package static assets, Ent is
the recommended ORM, and Swag with
gin-swagger the recommended way
to document your API).
To make your life even easier, there are also helpers for
request arguments, as well as for environment
variables.
To get started with a new project, initialize a new Go module with go mod init ...
and create a cmd/.../main.go
file according to the
example:
package main
import (
"codeberg.org/momar/webapp-boilerplate"
"codeberg.org/momar/webapp-boilerplate/environment"
"database/sql"
"github.com/gin-gonic/gin"
"github.com/markbates/pkger"
"github.com/rs/zerolog/log"
"io/ioutil"
".../ent"
)
//go:generate pkger
var db *ent.Client
var prefix = env.Read("PREFIX").Default("example-").AsString().Get()
func main() {
// Open static files
staticFiles, err := pkger.Open("/")
boilerplate.Must(err)
app := boilerplate.CreateApp().
WithLogging().
WithDatabase(func(driverName string, dataSourceName string) (err error) {
// Connect to the database
log.Info().Str("driver", driverName).Str("dataSource", dataSourceName).Msg("Connecting to database.")
db, err = ent.Open(driverName, dataSourceName)
return
}, func(string, string) (err error) {
// Apply schema
return db.Schema.Create(context.Background());
}).
WithGin().
WithStatic("/", staticFiles, true)
app.Engine.GET("/api/:key", func(c *gin.Context) {
// Retrieve value from database
value := db.Data.
GetX(c, prefix + c.Param(key)).
Value
c.String(200, value)
})
app.Engine.PUT("/api/:key", func(c *gin.Context) {
// Read body
value, err := ioutil.ReadAll(c.Request.Body)
boilerplate.Must(err)
// Try to update existing field in database
n := db.Data.
UpdateID(prefix + c.Param("key")).
SetValue(string(value)).
SaveX(c)
if n == 0 {
// If no field exist, create a new one
db.Data.Create().
SetID(prefix + c.Param("key")).
SetValue(string(value)).
SaveX(c)
}
c.String(200, "ok")
})
app.Start()
}
Additionally, you should copy & adjust the .gitignore
, Dockerfile
and docker-compose.yml
from this repository to get a clean & fully
packaged web application.
You can also create a symlink from .dockerignore
to .gitignore
(ln -s .gitignore .dockerignore
) to use the same ignore configuration for
both Git and Docker.
When creating an API, you should create your own error handling function
for all routes that are using Ent (see error-handling.go in
wuks/reservator
for an example) - in this project, you can also find an example on how
to do API documentation with Swag & gin-swagger.
Available methods
WithLogging()
Sets up Zerolog for nice console output & improved Gin logging.
You can use the following environment variables to modify the runtime
behaviour:
LOG_LEVEL
: minimum level for logging, available values are trace
,
debug
, info
, warn
and error
, the default is info
. For
trace
and debug
, Gin is set to debug mode
LOG_FORMAT
: format for logging, either json
or console
, the latter
is the default value.
WithDatabase(...func(driverName string, dataSourceName string) error)
Parses environment variables to determine the database connection &
calls the supplied functions with the driverName
& dataSourceName
as
expected by database/sql
. It currently supports MySQL, PostgreSQL and
SQLite, using the following environment variables:
SQLITE_DATABASE
: filename of the SQLite3 database. By default, an
in-memory SQLite3 database will be used!
MYSQL_DATABASE
: database name for MySQL. Required when using
MySQL!
MYSQL_HOST
: hostname of the MySQL server, defaults to 127.0.0.1
.
MYSQL_PORT
: port of the MySQL server, defaults to 3306
.
MYSQL_USER
: username for the MySQL connection, defaults to root
.
MYSQL_PASSWORD
: password for the MySQL connection, defaults to
either the value of MYSQL_ROOT_PASSWORD
if the user is root
, or an
empty string.
POSTGRES_DB
: database name for MySQL. Required when using
PostgreSQL!
POSTGRES_HOST
: hostname of the PostgreSQL server, defaults to 127.0.0.1
.
POSTGRES_PORT
: port of the PostgreSQL server, defaults to 5432
.
POSTGRES_SSLMODE
: encryption mode for the connection, defaults to
disable
. See
pq documentation
for more information.
POSTGRES_USER
: username for the PostgreSQL connection, defaults to
postgres
.
POSTGRES_PASSWORD
: password for the PostgreSQL connection, defaults to
an empty string.
WithGin()
Sets up a Gin engine with a logger (if LOG_LEVEL
is at least debug
)
and recovery & handling of panic()
(send 500 & log only to STDOUT) and
c.AbortWithError()
(send the error to the client).
HOST
: the hostname to listen on, defaults to [::]
(everywhere).
PORT
: the port to listen on, defaults to 80
if os.Geteuid()
is
0
(root), otherwise 8080
.
WithStatic(prefix, fs, allowIndex)
Adds a static file middleware to the Gin engine at the specified prefix.
As opposed to the equivalent Gin functions, this will not block
subordinate routes from being assigned.
Start()
Start the Gin webserver.
Future
- Full example application
- Recommendation for Authentication (JWT, OAuth2, LDAP, SAML) &
Permissions
- Recommendation for i18n
- Recommendation for admin/config dashboards (like Django)
- Documentation for adding a Vue 3 frontend
- Kubernetes support