Golang TOML Configs Example: MySQL Connection
Let's have a look at how to split application code and configuration for your Golang app. As an example I'd like to show how to use TOML to load your database configuration.
I came to write this because I was dabbling with exporting WordPress posts to TOML front matter / hugo and needed to be able to swap out database servers for development and production servers.
TLDR; If you want to skip all the jabber and code explanation, just scroll straight to the bottom.
Golang TOML parsing
TOML is a file format, a bit like JSON, that supports a simple and human readable key-value store, perfect for config files.To parse TOML, I tend to use BurntSushi/toml, because it's a solid and simple library and supports reading files without writing your own Reader.
My example config file:
# filename: config.toml
[database]
server = "127.0.0.1"
port = "3306"
database = "jonathanmh_com"
user = "jonathanmh"
password = "some_super_strange_long_password"
[output]
directory = "./output"
format = "toml"
Let's have a look at which struct we could parse that into:
type Config struct {
Output output
Database database
}
type database struct {
Server string
Port string
Database string
User string
Password string
}
type output struct {
Directory string
Format string
}
In order to parse the config file into the struct and its to sub-structs, we merely need to create a new variable for the config, in this case conf
and use toml.DecodeFile
:
var conf Config
if _, err := toml.DecodeFile("./config.toml", &conf); err != nil {
fmt.Println(err)
}
fmt.Printf("%#v\n", conf)
Golang String Interpolation
In order to make sure you have the right order and format and instead of using the+
operator and have a big mess in your configuration sensitive code, let's go ahead and make use of fmt.Sprintf
which basically works like Printf
, but lets you save the output to a variable.
connString := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s", conf.Database.User, conf.Database.Password, conf.Database.Server, conf.Database.Port, conf.Database.Database)
db, err := sql.Open("mysql", connString)
if err != nil {
fmt.Println(err.Error())
}
defer db.Close()
Now let's go ahead and test the connection with a simple query (that doesn't require you to create any tables)
var count int
err = db.QueryRow("SELECT 1 + 1").Scan(&count)
if err != nil {
panic(err)
}
fmt.Println(count)
The output should be 2
, if you configuration is correct, the database server running and so on.
Summary
That's it! A quick and pragmatic example how to use config files in TOML to save things like your database configuration in your Go(lang) project! As a quick tip, config files should not be tracked with git, so I would always put theconfig.toml
into the .gitignore
file, but keep a config.example.toml
around that illustrates how it works and which values need to be filled in.
How to you manage environment and configuration? Let me know in the comments!
See the full source below, don't forget the config.toml
further up ;)
package main
import(
"database/sql"
"fmt"
_ "github.com/go-sql-driver/mysql"
"github.com/BurntSushi/toml"
)
type Config struct {
Output output
Database database
}
type database struct {
Server string
Port string
Database string
User string
Password string
}
type output struct {
Directory string
Format string
}
func main(){
var conf Config
if _, err := toml.DecodeFile("./config.toml", &conf); err != nil {
fmt.Println(err)
}
fmt.Printf("%#v\n", conf)
connString := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s", conf.Database.User, conf.Database.Password, conf.Database.Server, conf.Database.Port, conf.Database.Database)
db, err := sql.Open("mysql", connString)
if err != nil {
fmt.Println(err.Error())
}
defer db.Close()
var count int
err = db.QueryRow("SELECT 1 + 1").Scan(&count)
if err != nil {
panic(err)
}
fmt.Println(count)
}