Documentation
¶
Overview ¶
Package database provides database integration middleware for fursy HTTP router.
This package provides:
- DB wrapper for *sql.DB with context support
- Middleware to share database connection across handlers
- Transaction helpers with auto-commit/rollback
- Context integration via c.DB() method
Example:
import (
"database/sql"
"github.com/coregx/fursy"
"github.com/coregx/fursy/plugins/database"
_ "github.com/lib/pq" // PostgreSQL driver
)
sqlDB, _ := sql.Open("postgres", dsn)
db := database.NewDB(sqlDB)
router := fursy.New()
router.Use(database.Middleware(db))
router.GET("/users/:id", func(c *fursy.Context) error {
db := c.DB()
var user User
err := db.QueryRow(c.Request.Context(),
"SELECT * FROM users WHERE id = $1", c.Param("id")).
Scan(&user.ID, &user.Name)
if err != nil {
return c.Problem(fursy.NotFound("User not found"))
}
return c.JSON(200, user)
})
Index ¶
- func Middleware(db *DB) fursy.HandlerFunc
- func TxMiddleware(db *DB) fursy.HandlerFunc
- func WithTx(ctx context.Context, db *DB, fn func(*Tx) error) error
- type DB
- func (d *DB) BeginTx(ctx context.Context, opts *sql.TxOptions) (*Tx, error)
- func (d *DB) Close() error
- func (d *DB) DB() *sql.DB
- func (d *DB) Exec(ctx context.Context, query string, args ...any) (sql.Result, error)
- func (d *DB) Ping(ctx context.Context) error
- func (d *DB) Query(ctx context.Context, query string, args ...any) (*sql.Rows, error)
- func (d *DB) QueryRow(ctx context.Context, query string, args ...any) *sql.Row
- type Tx
- func (t *Tx) Commit() error
- func (t *Tx) Exec(ctx context.Context, query string, args ...any) (sql.Result, error)
- func (t *Tx) Query(ctx context.Context, query string, args ...any) (*sql.Rows, error)
- func (t *Tx) QueryRow(ctx context.Context, query string, args ...any) *sql.Row
- func (t *Tx) Rollback() error
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func Middleware ¶
func Middleware(db *DB) fursy.HandlerFunc
Middleware creates a middleware that stores the database in the request context.
This allows handlers to access the database via c.DB() method.
Example:
db := database.NewDB(sqlDB)
router.Use(database.Middleware(db))
router.GET("/users", func(c *fursy.Context) error {
db := c.DB()
// Use db for queries...
return nil
})
func TxMiddleware ¶
func TxMiddleware(db *DB) fursy.HandlerFunc
TxMiddleware creates a middleware that wraps each request in a database transaction.
The transaction is automatically committed if the handler succeeds (returns nil), or rolled back if the handler returns an error.
This is useful for endpoints that require transactional guarantees.
Example:
// Apply to specific routes that need transactions:
txGroup := router.Group("/api/v1")
txGroup.Use(database.Middleware(db))
txGroup.Use(database.TxMiddleware(db))
txGroup.POST("/users", func(c *fursy.Context) error {
tx, _ := database.GetTx(c)
// Use tx for all database operations
// Auto-commit on success, auto-rollback on error
return nil
})
func WithTx ¶
WithTx executes a function within a database transaction.
If the function returns an error, the transaction is rolled back. Otherwise, the transaction is committed.
This is a convenience helper that handles transaction lifecycle automatically.
Example:
err := database.WithTx(ctx, db, func(tx *database.Tx) error {
_, err := tx.Exec(ctx, "INSERT INTO users (name) VALUES ($1)", "Bob")
if err != nil {
return err // Automatic rollback
}
_, err = tx.Exec(ctx, "INSERT INTO audit (action) VALUES ($1)", "user_created")
return err // Automatic commit on nil error
})
Types ¶
type DB ¶
type DB struct {
// contains filtered or unexported fields
}
DB wraps a *sql.DB connection with context support.
It provides a thin wrapper around database/sql that integrates with fursy's context and middleware system.
func GetDB ¶
GetDB retrieves the database from the context.
Returns (nil, false) if database middleware is not configured.
Example:
db, ok := database.GetDB(c)
if !ok {
return c.Problem(fursy.InternalServerError("Database not configured"))
}
func GetDBOrError ¶
GetDBOrError retrieves the database from the context or returns an RFC 9457 error.
This is a convenience helper that combines GetDB() with error handling. Returns InternalServerError (500) if database middleware is not configured.
This is the recommended approach for production APIs where database misconfiguration should return a proper error response.
Example:
router.GET("/users", func(c *fursy.Context) error {
db, err := database.GetDBOrError(c)
if err != nil {
return c.Problem(err.(fursy.Problem))
}
rows, err := db.Query(c.Request.Context(), "SELECT * FROM users")
// ...
return c.JSON(200, users)
})
func MustGetDB ¶
MustGetDB retrieves the database from the context or panics.
This is a convenience helper for handlers where database is required. Panics with a descriptive message if database middleware is not configured.
Use this in handlers where database absence indicates a programming error (i.e., middleware misconfiguration), not a runtime error.
Example:
router.GET("/users", func(c *fursy.Context) error {
db := database.MustGetDB(c) // Panic if DB not configured
rows, err := db.Query(c.Request.Context(), "SELECT * FROM users")
// ...
return c.JSON(200, users)
})
For production APIs with proper error handling, use GetDBOrError() instead.
func NewDB ¶
NewDB creates a new DB wrapper around a *sql.DB connection.
Example:
sqlDB, err := sql.Open("postgres", dsn)
if err != nil {
log.Fatal(err)
}
db := database.NewDB(sqlDB)
func (*DB) BeginTx ¶
BeginTx starts a new database transaction.
The provided context is used until the transaction is committed or rolled back. If the context is canceled, the sql package will roll back the transaction.
Example:
tx, err := db.BeginTx(ctx, nil)
if err != nil {
return err
}
defer tx.Rollback() // Rollback if not committed
// ... perform operations ...
return tx.Commit()
func (*DB) Close ¶
Close closes the database connection.
It is rare to Close a DB, as the DB handle is meant to be long-lived and shared between many goroutines.
func (*DB) DB ¶
DB returns the underlying *sql.DB connection.
This is useful when you need to access the raw database/sql API.
Example:
stats := db.DB().Stats()
func (*DB) Exec ¶
Exec executes a query without returning rows.
Example:
result, err := db.Exec(ctx, "DELETE FROM users WHERE id = $1", userID)
if err != nil {
return err
}
rowsAffected, _ := result.RowsAffected()
func (*DB) Ping ¶
Ping verifies a connection to the database is still alive.
Example:
if err := db.Ping(ctx); err != nil {
log.Fatal("Database connection lost:", err)
}
func (*DB) Query ¶
Query executes a query that returns rows.
Example:
rows, err := db.Query(ctx, "SELECT id, name FROM users")
if err != nil {
return err
}
defer rows.Close()
for rows.Next() {
var id int
var name string
rows.Scan(&id, &name)
}
type Tx ¶
type Tx struct {
// contains filtered or unexported fields
}
Tx wraps a *sql.Tx transaction with context support.
Transactions provide ACID guarantees for database operations. All operations within a transaction are atomic - they either all succeed (commit) or all fail (rollback).
func GetTx ¶
GetTx retrieves the transaction from the context.
Returns (nil, false) if TxMiddleware is not configured for this request.
Example:
tx, ok := database.GetTx(c)
if !ok {
return c.Problem(fursy.InternalServerError("Transaction not available"))
}
_, err := tx.Exec(c.Request.Context(), "INSERT INTO ...")
func GetTxOrError ¶
GetTxOrError retrieves the transaction from the context or returns an RFC 9457 error.
This is a convenience helper that combines GetTx() with error handling. Returns InternalServerError (500) if TxMiddleware is not configured.
This is the recommended approach for production APIs where transaction unavailability should return a proper error response.
Example:
txGroup.POST("/transfer", func(c *fursy.Context) error {
tx, err := database.GetTxOrError(c)
if err != nil {
return c.Problem(err.(fursy.Problem))
}
_, err = tx.Exec(c.Request.Context(), "UPDATE accounts SET ...")
return err
})
func MustGetTx ¶
MustGetTx retrieves the transaction from the context or panics.
This is a convenience helper for handlers where transaction is required. Panics with a descriptive message if TxMiddleware is not configured.
Use this in handlers where transaction absence indicates a programming error (i.e., middleware misconfiguration), not a runtime error.
Example:
txGroup := router.Group("/api")
txGroup.Use(database.TxMiddleware(db))
txGroup.POST("/transfer", func(c *fursy.Context) error {
tx := database.MustGetTx(c) // Panic if TxMiddleware not configured
_, err := tx.Exec(c.Request.Context(), "UPDATE accounts SET ...")
return err
})
For production APIs with proper error handling, use GetTxOrError() instead.
func (*Tx) Commit ¶
Commit commits the transaction.
Returns an error if the transaction has already been committed or rolled back.
func (*Tx) Exec ¶
Exec executes a query without returning rows within the transaction.
Example:
_, err := tx.Exec(ctx, "INSERT INTO users (name) VALUES ($1)", "Alice")
func (*Tx) Query ¶
Query executes a query that returns rows within the transaction.
Example:
rows, err := tx.Query(ctx, "SELECT id, name FROM users")
if err != nil {
return err
}
defer rows.Close()