diff --git a/cmd/api/main.go b/cmd/api/main.go index 2ece088..3001895 100644 --- a/cmd/api/main.go +++ b/cmd/api/main.go @@ -1,12 +1,17 @@ package main import ( + "context" + "database/sql" "flag" "fmt" "log/slog" "net/http" "os" "time" + + // Import the pq driver so that it can register itself with the database/sql package. Note that we alias this import to the blank identifier, to stop the Go compiler complaining that the package isn't being used. + _ "github.com/lib/pq" ) /* @@ -22,9 +27,13 @@ For now, the only configuration settings will be the network port that we want t and the name of the current operating environment for the application (development, staging, production, etc.) We'll read in the configuration settings from command-line flags when the application starts. */ +// Add a db struct field to hold the configuration settings for our database connection pool. For now, this only holds the DSN, which we will read in from a command-line flag. type config struct { port int env string + db struct { + dsn string + } } /* @@ -46,11 +55,29 @@ func main() { */ flag.IntVar(&cfg.port, "port", 4000, "API server port") flag.StringVar(&cfg.env, "env", "development", "Environment (development|staging|production") + + // Read the DSN value from the db-dsn command-line flag into the config struct. We default to using our development DSN if no flag is provided. + // Use the value of the GREENLIGHT_DB_DSN environment variable as the default value for our db-dsn command-line flag. + flag.StringVar(&cfg.db.dsn, "db-dsn", os.Getenv("GREENLIGHT_DB_DSN"), "PostgreSQL DSN") + flag.Parse() // Initialize a new structured logger which writes log entries to standard out stream. logger := slog.New(slog.NewTextHandler(os.Stdout, nil)) + // Call the openDB() helper function to create the connection pool, passing in the config struct. If this returns an error, we log it and exit the application immediately. + db, err := openDB(cfg) + if err != nil { + logger.Error(err.Error()) + os.Exit(1) + } + + // Defer a call to db.Close() so that the connection pool is closed before the main() function exists + defer db.Close() + + // Also log a message to say that the connection pool has been successfully established. + logger.Info("database connection pool established") + // Declare an instance of the application struct, containing the config struct and the logger. app := &application{ config: cfg, @@ -74,9 +101,33 @@ func main() { // Start the HTTP server. logger.Info("starting server", "addr", srv.Addr, "env", cfg.env) - err := srv.ListenAndServe() + // Because the err variable is now already declared in the code above, we need to user = operator here, instead of the := operator. + err = srv.ListenAndServe() if err != nil { logger.Error(err.Error()) os.Exit(1) } } + +// openDB() function returns a sql.DB connection pool. +func openDB(cfg config) (*sql.DB, error) { + // Use sql.Open() to create an empty connection pool, using the DSN from the config struct. + db, err := sql.Open("postgres", cfg.db.dsn) + if err != nil { + return nil, err + } + + // Create a context with a 5-second timeout deadline. + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + + // Use PingContext() to establish a new connection to the database, passing in the context we created above as parameter. If the connection couldn't be established successfully within the 5-second deadline, then this will return an error. If we get this error, or any other, we close the connection pool and return the error. + err = db.PingContext(ctx) + if err != nil { + db.Close() + return nil, err + } + + // Return the sql.DB connection pool. + return db, nil +} diff --git a/go.mod b/go.mod index e9258b3..dbe9583 100644 --- a/go.mod +++ b/go.mod @@ -2,4 +2,7 @@ module greenlight.craftr.fr go 1.25.1 -require github.com/julienschmidt/httprouter v1.3.0 // indirect +require ( + github.com/julienschmidt/httprouter v1.3.0 // indirect + github.com/lib/pq v1.10.9 // indirect +) diff --git a/go.sum b/go.sum index 096c54e..0a18b5d 100644 --- a/go.sum +++ b/go.sum @@ -1,2 +1,4 @@ github.com/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4dN7jwJOQ1U= github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= +github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= +github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=