|
3 months ago | |
---|---|---|
builders | 5 months ago | |
cmd/example | 5 months ago | |
env | 5 months ago | |
parsable | 5 months ago | |
request | 5 months ago | |
.gitignore | 5 months ago | |
Dockerfile | 5 months ago | |
LICENSE | 5 months ago | |
README.md | 5 months ago | |
builder.go | 5 months ago | |
database.go | 3 months ago | |
docker-compose.yml | 5 months ago | |
gin.go | 5 months ago | |
go.mod | 3 months ago | |
go.sum | 5 months ago | |
helpers.go | 5 months ago | |
log.go | 5 months ago | |
static.go | 5 months ago |
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.
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 modeLOG_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.