Server configuration
Tags: go
Published: 2021-06-24 17:25:11

Currently we have hardcoded values for server address and port. Same goes for database options required for connecting to our database. That is obviously not a great solution, so let's extract this configuration to ENV variables and add logic to read values from there. First we will create new directory `internal/conf/` and there we will create `conf.go` file with content:

```go
package conf

import (
  "log"
  "os"
  "strconv"
)

const (
  hostKey       = "RGB_HOST"
  portKey       = "RGB_PORT"
  dbHostKey     = "RGB_DB_HOST"
  dbPortKey     = "RGB_DB_PORT"
  dbNameKey     = "RGB_DB_NAME"
  dbUserKey     = "RGB_DB_USER"
  dbPasswordKey = "RGB_DB_PASSWORD"
)

type Config struct {
  Host       string
  Port       string
  DbHost     string
  DbPort     string
  DbName     string
  DbUser     string
  DbPassword string
}

func NewConfig() Config {
  host, ok := os.LookupEnv(hostKey)
  if !ok || host == "" {
    logAndPanic(hostKey)
  }

  port, ok := os.LookupEnv(portKey)
  if !ok || port == "" {
    if _, err := strconv.Atoi(port); err != nil {
      logAndPanic(portKey)
    }
  }

  dbHost, ok := os.LookupEnv(dbHostKey)
  if !ok || dbHost == "" {
    logAndPanic(dbHostKey)
  }

  dbPort, ok := os.LookupEnv(dbPortKey)
  if !ok || dbPort == "" {
    if _, err := strconv.Atoi(dbPort); err != nil {
      logAndPanic(dbPortKey)
    }
  }

  dbName, ok := os.LookupEnv(dbNameKey)
  if !ok || dbName == "" {
    logAndPanic(dbNameKey)
  }

  dbUser, ok := os.LookupEnv(dbUserKey)
  if !ok || dbUser == "" {
    logAndPanic(dbUserKey)
  }

  dbPassword, ok := os.LookupEnv(dbPasswordKey)
  if !ok || dbPassword == "" {
    logAndPanic(dbPasswordKey)
  }

  return Config{
    Host:       host,
    Port:       port,
    DbHost:     dbHost,
    DbPort:     dbPort,
    DbName:     dbName,
    DbUser:     dbUser,
    DbPassword: dbPassword,
  }
}

func logAndPanic(envVar string) {
  log.Println("ENV variable not set or value not valid: ", envVar)
  panic(envVar)
}
```

With configuration logic implemented, all that is left to do is use it. For that we will need to slightly refactor our existing code. Our files will then look like this:

`internal/database/database.go`:

```go
package database

import (
  "rgb/internal/conf"

  "github.com/go-pg/pg/v10"
)

func NewDBOptions(cfg conf.Config) *pg.Options {
  return &pg.Options{
    Addr:     cfg.DbHost + ":" + cfg.DbPort,
    Database: cfg.DbName,
    User:     cfg.DbUser,
    Password: cfg.DbPassword,
  }
}
```

`internal/server/server.go`:

```go
package server

import (
  "rgb/internal/conf"
  "rgb/internal/database"
  "rgb/internal/store"
)

func Start(cfg conf.Config) {
  store.SetDBConnection(database.NewDBOptions(cfg))

  router := setRouter()

  // Start listening and serving requests
  router.Run(":8080")
}
```

`cmd/rgb/main.go`:

```go
package main

import (
  "rgb/internal/conf"
  "rgb/internal/server"
)

func main() {
  server.Start(conf.NewConfig())
}
```

One more change is required in `migrations/main.go` file. Just import `rgb/internal/conf` package and change line

```go
store.SetDBConnection(database.NewDBOptions())
```

to

```go
store.SetDBConnection(database.NewDBOptions(conf.NewConfig()))
```

We are now ready to read ENV variables required for our configuration. But one more thing is missing. We need to feed those values to ENV. To do that let's create new file in root project directory named `.env`:

```bash
export RGB_HOST=0.0.0.0
export RGB_PORT=8080
export RGB_DB_HOST=localhost
export RGB_DB_PORT=5432
export RGB_DB_NAME=rgb
export RGB_DB_USER=postgres
export RGB_DB_PASSWORD=postgres
```

We can feed these values to ENV using `source` command. So, now we can start our server like this:

```bash
source .env
go run cmd/rgb/main.go
```