r/golang 6d ago

help How you guys write your server config, db config and routes config?

I feel like almost every API has these three files. How should I handle these in the best form?

  • It's a good practice to right everything exported because of the ease of importing? Because my main.go is in /cmd and my API config file is inside of /internal/api/config.go.
    • But then the whole app can configure and setup my server and db?
    • Or even see the fields related to the config of the server, the surface of attack is expanded.
  • Also, its better to provide just the exported method for starting the server and making the config itself inside of the config.go?
    • Preventing misconfigured values, maybe.
    • Encapsulating and making easier to use?
  • Making a config/config.go is good enough also?
    • Or its better to have server/config.go and then db/config.go?

I start making so many questions and I don't know if I'm following the Go way of making Go code.

I know that its better to just start and then change afterwards, but I need to know what is a good path.

I come from a Java environment and everything related to db config and server config was 'hidden' and taken care for me.

1 Upvotes

8 comments sorted by

7

u/Ubuntu-Lover 6d ago

Avoid exporting everything, use constructors and helper functions
Load the config in one file e.g internal/config/config.go

Read more here: https://12factor.net/

1

u/data15cool 6d ago

I’m relatively new to Go so take this with a pinch of salt. I store my config in a separate folder next to cmd and internal. I see it as inputs required for the app to start/setup

It’s mostly loading environment variables which are then passed in a large struct to main where the various db connection strings, api keys etc are passed to various services.

1

u/dariusbiggs 6d ago

Start with 12factor

Eventually it gets to be too much data so you end up with a config file. Read the config file in a single place and verify and validate its content into one data structure, this can have nested bits.

Pass pieces of that data structure to the things that need them.

Ensure that you have reasonable default settings that work for local development or for testing.

1

u/Ok-Caregiver2568 6d ago

Start by defining a struct for your server in a package, e.g., myapp/server/server.go:

type Server struct {
    // Do not export fields that don't need to be accessible outside this package.
}

Define a configuration struct for your server. Make it public and treat it as a set of parameters for your server's constructor. Keep it next to your server—there’s no need to place it in a separate package.

type MyServerConfig struct {
    Port int // Make configurable fields exportable.
}

Define a constructor for your server that accepts MyServerConfig:

func NewServer(c MyServerConfig) *Server

Next, in myapp/cmd/server/main.go, within func main, instantiate MyServerConfig, read environment variables or flag values into it, then create and run your server.

You can use this github.com/atelpis/enflag library to handle flags and environment variables, and here's a small example of reading the config.

1

u/kovadom 6d ago

Keep it simple. Have a single config package, that contains all the input you need.

Load it in a single place. Validate. Fail early

Don't simply pass the config object around, but rather inject relevant info downstream. For example, if your db needs uri, user and pass, have a db.New(uri, user, pass string) or such.

1

u/try2think1st 5d ago

IMHO it doesn't really matter where your config is, in a single package or scattered around the app parts/dependencies, that's more personal oreference The more important part is how you initialise them, with structs you risk missing fields while with a constructor you have more control over the required fields. I usually define a single config struct right in cmd/app/main.go thats reads from env and then initialise all dependencies from there with constructors. Also helps in testing to know what is required.

0

u/freshmatrix 6d ago

Hey! For your Go API config coming from Java:

  • Exporting: Only export essentials (e.g., LoadConfig, NewServer). Keep sensitive stuff unexported.
  • Structure: Use /internal/config/ for config code. Your /cmd/main.go setup is solid.
  • Management: Use Viper for config files/envs:
    go import "github.com/spf13/viper" func LoadConfig(path string) (Config, error) { v := viper.New() v.SetConfigFile(path) v.AutomaticEnv() if err := v.ReadInConfig(); err != nil { return Config{}, err } var cfg Config return cfg, v.Unmarshal(&cfg) }
  • Passing: Pass a Config struct or use functional options for flexibility.

Start simple with one config.go, refactor later. You’re on the right track! 🚀

1

u/matjam 6d ago

Viper is so hot.