Client Lifecycle
This guide covers initializing the Phi client, configuring connection pool bounds, registering extension hooks during application startup, statement caching, and executing graceful shutdowns.
1. Opening a Client Connection
Initialize the generated client using phi.Open(driverName, dataSourceName):
package main
import (
"log"
_ "github.com/lib/pq" // Import SQL driver
"myproject/phi"
)
func main() {
// Open connection pool
db, err := phi.Open("postgres", "postgres://user:pass@localhost:5432/dbname?sslmode=disable")
if err != nil {
log.Fatalf("failed to connect to database: %v", err)
}
defer db.Close() // Guarantee clean shutdown
}2. Connection Pool Configuration
db.Raw() returns the underlying *sql.DB handle. Use it during startup to configure standard Go database connection pool parameters:
db.Raw().SetMaxOpenConns(25)
db.Raw().SetMaxIdleConns(5)
db.Raw().SetConnMaxLifetime(5 * time.Minute)
db.Raw().SetConnMaxIdleTime(1 * time.Minute)3. Registering Extension Hooks
Register middleware extensions (.Use(...)) during application initialization before serving queries. Registered extensions are stored in thread-safe delegates and inherited by transactions:
func initDB() (*phi.DB, error) {
db, err := phi.Open("sqlite3", "file:app.db?cache=shared&_pragma=foreign_keys(1)")
if err != nil {
return nil, err
}
// Register global audit hook on user delegate
db.User.Use(user.Extension{
Create: func(ctx context.Context, args *phi.UserCreateArgs, next phi.UserCreateQuery) (*phi.User, error) {
args.Data.Email = strings.ToLower(args.Data.Email)
return next(ctx, args)
},
})
return db, nil
}4. Prepared Statement Caching
Phi includes a thread-safe internal prepared statement cache (stmtCache). Frequently executed query shapes reuse prepared statements automatically, minimizing database parsing overhead and boosting throughput.
5. Graceful Shutdown (`db.Close()`)
When your application terminates, call db.Close() to flush prepared statement caches, release active database connections, and shut down the connection pool cleanly:
func main() {
db, err := phi.Open("postgres", connString)
if err != nil {
log.Fatal(err)
}
// Graceful shutdown on OS signals
sigChan := make(chan os.Signal, 1)
signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
go func() {
<-sigChan
log.Println("Shutting down database client...")
if err := db.Close(); err != nil {
log.Printf("Error closing database connection: %v", err)
}
os.Exit(0)
}()
}