mirror of https://github.com/gofiber/fiber.git
641 lines
13 KiB
Markdown
641 lines
13 KiB
Markdown
# State Management
|
||
|
||
This document details the state management functionality provided by Fiber, a thread-safe global key–value store used to store application dependencies and runtime data. The implementation is based on Go's `sync.Map`, ensuring concurrency safety.
|
||
|
||
Below is the detailed description of all public methods and usage examples.
|
||
|
||
## State Type
|
||
|
||
`State` is a key–value store built on top of `sync.Map`. It allows storage and retrieval of dependencies and configurations in a Fiber application as well as thread–safe access to runtime data.
|
||
|
||
### Definition
|
||
|
||
```go
|
||
// State is a key–value store for Fiber's app, used as a global storage for the app's dependencies.
|
||
// It is a thread–safe implementation of a map[string]any, using sync.Map.
|
||
type State struct {
|
||
dependencies sync.Map
|
||
}
|
||
```
|
||
|
||
## Methods on State
|
||
|
||
### Set
|
||
|
||
Set adds or updates a key–value pair in the State.
|
||
|
||
```go
|
||
// Set adds or updates a key–value pair in the State.
|
||
func (s *State) Set(key string, value any)
|
||
```
|
||
|
||
**Usage Example:**
|
||
|
||
```go
|
||
app.State().Set("appName", "My Fiber App")
|
||
```
|
||
|
||
### Get
|
||
|
||
Get retrieves a value from the State.
|
||
|
||
```go title="Signature"
|
||
func (s *State) Get(key string) (any, bool)
|
||
```
|
||
|
||
**Usage Example:**
|
||
|
||
```go
|
||
value, ok := app.State().Get("appName")
|
||
if ok {
|
||
fmt.Println("App Name:", value)
|
||
}
|
||
```
|
||
|
||
### MustGet
|
||
|
||
MustGet retrieves a value from the State and panics if the key is not found.
|
||
|
||
```go title="Signature"
|
||
func (s *State) MustGet(key string) any
|
||
```
|
||
|
||
**Usage Example:**
|
||
|
||
```go
|
||
appName := app.State().MustGet("appName")
|
||
fmt.Println("App Name:", appName)
|
||
```
|
||
|
||
### Has
|
||
|
||
Has checks if a key exists in the State.
|
||
|
||
```go title="Signature"s
|
||
func (s *State) Has(key string) bool
|
||
```
|
||
|
||
**Usage Example:**
|
||
|
||
```go
|
||
if app.State().Has("appName") {
|
||
fmt.Println("App Name is set.")
|
||
}
|
||
```
|
||
|
||
### Delete
|
||
|
||
Delete removes a key–value pair from the State.
|
||
|
||
```go title="Signature"
|
||
func (s *State) Delete(key string)
|
||
```
|
||
|
||
**Usage Example:**
|
||
|
||
```go
|
||
app.State().Delete("obsoleteKey")
|
||
```
|
||
|
||
### Reset
|
||
|
||
Reset removes all keys from the State.
|
||
|
||
```go title="Signature"
|
||
func (s *State) Reset()
|
||
```
|
||
|
||
**Usage Example:**
|
||
|
||
```go
|
||
app.State().Reset()
|
||
```
|
||
|
||
### Keys
|
||
|
||
Keys returns a slice containing all keys present in the State.
|
||
|
||
```go title="Signature"
|
||
func (s *State) Keys() []string
|
||
```
|
||
|
||
**Usage Example:**
|
||
|
||
```go
|
||
keys := app.State().Keys()
|
||
fmt.Println("State Keys:", keys)
|
||
```
|
||
|
||
### Len
|
||
|
||
Len returns the number of keys in the State.
|
||
|
||
```go
|
||
// Len returns the number of keys in the State.
|
||
func (s *State) Len() int
|
||
```
|
||
|
||
**Usage Example:**
|
||
|
||
```go
|
||
fmt.Printf("Total State Entries: %d\n", app.State().Len())
|
||
```
|
||
|
||
### GetString
|
||
|
||
GetString retrieves a string value from the State. It returns the string and a boolean indicating a successful type assertion.
|
||
|
||
```go title="Signature"
|
||
func (s *State) GetString(key string) (string, bool)
|
||
```
|
||
|
||
**Usage Example:**
|
||
|
||
```go
|
||
if appName, ok := app.State().GetString("appName"); ok {
|
||
fmt.Println("App Name:", appName)
|
||
}
|
||
```
|
||
|
||
### GetInt
|
||
|
||
GetInt retrieves an integer value from the State. It returns the int and a boolean indicating a successful type assertion.
|
||
|
||
```go title="Signature"
|
||
func (s *State) GetInt(key string) (int, bool)
|
||
```
|
||
|
||
**Usage Example:**
|
||
|
||
```go
|
||
if count, ok := app.State().GetInt("userCount"); ok {
|
||
fmt.Printf("User Count: %d\n", count)
|
||
}
|
||
```
|
||
|
||
### GetBool
|
||
|
||
GetBool retrieves a boolean value from the State. It returns the bool and a boolean indicating a successful type assertion.
|
||
|
||
```go title="Signature"
|
||
func (s *State) GetBool(key string) (value, bool)
|
||
```
|
||
|
||
**Usage Example:**
|
||
|
||
```go
|
||
if debug, ok := app.State().GetBool("debugMode"); ok {
|
||
fmt.Printf("Debug Mode: %v\n", debug)
|
||
}
|
||
```
|
||
|
||
### GetFloat64
|
||
|
||
GetFloat64 retrieves a float64 value from the State. It returns the float64 and a boolean indicating a successful type assertion.
|
||
|
||
```go title="Signature"
|
||
func (s *State) GetFloat64(key string) (float64, bool)
|
||
```
|
||
|
||
**Usage Example:**
|
||
|
||
```go title="Signature"
|
||
if ratio, ok := app.State().GetFloat64("scalingFactor"); ok {
|
||
fmt.Printf("Scaling Factor: %f\n", ratio)
|
||
}
|
||
```
|
||
|
||
### GetUint
|
||
|
||
GetUint retrieves a `uint` value from the State.
|
||
|
||
```go title="Signature"
|
||
func (s *State) GetUint(key string) (uint, bool)
|
||
```
|
||
|
||
**Usage Example:**
|
||
|
||
```go
|
||
if val, ok := app.State().GetUint("maxConnections"); ok {
|
||
fmt.Printf("Max Connections: %d\n", val)
|
||
}
|
||
```
|
||
|
||
### GetInt8
|
||
|
||
GetInt8 retrieves an `int8` value from the State.
|
||
|
||
```go title="Signature"
|
||
func (s *State) GetInt8(key string) (int8, bool)
|
||
```
|
||
|
||
**Usage Example:**
|
||
|
||
```go
|
||
if val, ok := app.State().GetInt8("threshold"); ok {
|
||
fmt.Printf("Threshold: %d\n", val)
|
||
}
|
||
```
|
||
|
||
### GetInt16
|
||
|
||
GetInt16 retrieves an `int16` value from the State.
|
||
|
||
```go title="Signature"
|
||
func (s *State) GetInt16(key string) (int16, bool)
|
||
```
|
||
|
||
**Usage Example:**
|
||
|
||
```go
|
||
if val, ok := app.State().GetInt16("minValue"); ok {
|
||
fmt.Printf("Minimum Value: %d\n", val)
|
||
}
|
||
```
|
||
|
||
### GetInt32
|
||
|
||
GetInt32 retrieves an `int32` value from the State.
|
||
|
||
```go title="Signature"
|
||
func (s *State) GetInt32(key string) (int32, bool)
|
||
```
|
||
|
||
**Usage Example:**
|
||
|
||
```go
|
||
if val, ok := app.State().GetInt32("portNumber"); ok {
|
||
fmt.Printf("Port Number: %d\n", val)
|
||
}
|
||
```
|
||
|
||
### GetInt64
|
||
|
||
GetInt64 retrieves an `int64` value from the State.
|
||
|
||
```go title="Signature"
|
||
func (s *State) GetInt64(key string) (int64, bool)
|
||
```
|
||
|
||
**Usage Example:**
|
||
|
||
```go
|
||
if val, ok := app.State().GetInt64("fileSize"); ok {
|
||
fmt.Printf("File Size: %d\n", val)
|
||
}
|
||
```
|
||
|
||
### GetUint8
|
||
|
||
GetUint8 retrieves a `uint8` value from the State.
|
||
|
||
```go title="Signature"
|
||
func (s *State) GetUint8(key string) (uint8, bool)
|
||
```
|
||
|
||
**Usage Example:**
|
||
|
||
```go
|
||
if val, ok := app.State().GetUint8("byteValue"); ok {
|
||
fmt.Printf("Byte Value: %d\n", val)
|
||
}
|
||
```
|
||
|
||
### GetUint16
|
||
|
||
GetUint16 retrieves a `uint16` value from the State.
|
||
|
||
```go title="Signature"
|
||
func (s *State) GetUint16(key string) (uint16, bool)
|
||
```
|
||
|
||
**Usage Example:**
|
||
|
||
```go
|
||
if val, ok := app.State().GetUint16("limit"); ok {
|
||
fmt.Printf("Limit: %d\n", val)
|
||
}
|
||
```
|
||
|
||
### GetUint32
|
||
|
||
GetUint32 retrieves a `uint32` value from the State.
|
||
|
||
```go title="Signature"
|
||
func (s *State) GetUint32(key string) (uint32, bool)
|
||
```
|
||
|
||
**Usage Example:**
|
||
|
||
```go
|
||
if val, ok := app.State().GetUint32("timeout"); ok {
|
||
fmt.Printf("Timeout: %d\n", val)
|
||
}
|
||
```
|
||
|
||
### GetUint64
|
||
|
||
GetUint64 retrieves a `uint64` value from the State.
|
||
|
||
```go title="Signature"
|
||
func (s *State) GetUint64(key string) (uint64, bool)
|
||
```
|
||
|
||
**Usage Example:**
|
||
|
||
```go
|
||
if val, ok := app.State().GetUint64("maxSize"); ok {
|
||
fmt.Printf("Max Size: %d\n", val)
|
||
}
|
||
```
|
||
|
||
### GetUintptr
|
||
|
||
GetUintptr retrieves a `uintptr` value from the State.
|
||
|
||
```go title="Signature"
|
||
func (s *State) GetUintptr(key string) (uintptr, bool)
|
||
```
|
||
|
||
**Usage Example:**
|
||
|
||
```go
|
||
if val, ok := app.State().GetUintptr("pointerValue"); ok {
|
||
fmt.Printf("Pointer Value: %d\n", val)
|
||
}
|
||
```
|
||
|
||
### GetFloat32
|
||
|
||
GetFloat32 retrieves a `float32` value from the State.
|
||
|
||
```go title="Signature"
|
||
func (s *State) GetFloat32(key string) (float32, bool)
|
||
```
|
||
|
||
**Usage Example:**
|
||
|
||
```go
|
||
if val, ok := app.State().GetFloat32("scalingFactor32"); ok {
|
||
fmt.Printf("Scaling Factor (float32): %f\n", val)
|
||
}
|
||
```
|
||
|
||
### GetComplex64
|
||
|
||
GetComplex64 retrieves a `complex64` value from the State.
|
||
|
||
```go title="Signature"
|
||
func (s *State) GetComplex64(key string) (complex64, bool)
|
||
```
|
||
|
||
**Usage Example:**
|
||
|
||
```go
|
||
if val, ok := app.State().GetComplex64("complexVal"); ok {
|
||
fmt.Printf("Complex Value (complex64): %v\n", val)
|
||
}
|
||
```
|
||
|
||
### GetComplex128
|
||
|
||
GetComplex128 retrieves a `complex128` value from the State.
|
||
|
||
```go title="Signature"
|
||
func (s *State) GetComplex128(key string) (complex128, bool)
|
||
```
|
||
|
||
**Usage Example:**
|
||
|
||
```go
|
||
if val, ok := app.State().GetComplex128("complexVal128"); ok {
|
||
fmt.Printf("Complex Value (complex128): %v\n", val)
|
||
}
|
||
```
|
||
|
||
## Generic Functions
|
||
|
||
Fiber provides generic functions to retrieve state values with type safety and fallback options.
|
||
|
||
### GetState
|
||
|
||
GetState retrieves a value from the State and casts it to the desired type. It returns the cast value and a boolean indicating if the cast was successful.
|
||
|
||
```go title="Signature"
|
||
func GetState[T any](s *State, key string) (T, bool)
|
||
```
|
||
|
||
**Usage Example:**
|
||
|
||
```go
|
||
// Retrieve an integer value safely.
|
||
userCount, ok := GetState[int](app.State(), "userCount")
|
||
if ok {
|
||
fmt.Printf("User Count: %d\n", userCount)
|
||
}
|
||
```
|
||
|
||
### MustGetState
|
||
|
||
MustGetState retrieves a value from the State and casts it to the desired type. It panics if the key is not found or if the type assertion fails.
|
||
|
||
```go title="Signature"
|
||
func MustGetState[T any](s *State, key string) T
|
||
```
|
||
|
||
**Usage Example:**
|
||
|
||
```go
|
||
// Retrieve the value or panic if it is not present.
|
||
config := MustGetState[string](app.State(), "configFile")
|
||
fmt.Println("Config File:", config)
|
||
```
|
||
|
||
### GetStateWithDefault
|
||
|
||
GetStateWithDefault retrieves a value from the State, casting it to the desired type. If the key is not present, it returns the provided default value.
|
||
|
||
```go title="Signature"
|
||
func GetStateWithDefault[T any](s *State, key string, defaultVal T) T
|
||
```
|
||
|
||
**Usage Example:**
|
||
|
||
```go
|
||
// Retrieve a value with a default fallback.
|
||
requestCount := GetStateWithDefault[int](app.State(), "requestCount", 0)
|
||
fmt.Printf("Request Count: %d\n", requestCount)
|
||
```
|
||
|
||
## Comprehensive Examples
|
||
|
||
### Example: Request Counter
|
||
|
||
This example demonstrates how to track the number of requests using the State.
|
||
|
||
```go
|
||
package main
|
||
|
||
import (
|
||
"fmt"
|
||
|
||
"github.com/gofiber/fiber/v3"
|
||
)
|
||
|
||
func main() {
|
||
app := fiber.New()
|
||
|
||
// Initialize state with a counter.
|
||
app.State().Set("requestCount", 0)
|
||
|
||
// Middleware: Increase counter for every request.
|
||
app.Use(func(c fiber.Ctx) error {
|
||
count, _ := c.App().State().GetInt("requestCount")
|
||
app.State().Set("requestCount", count+1)
|
||
return c.Next()
|
||
})
|
||
|
||
app.Get("/", func(c fiber.Ctx) error {
|
||
return c.SendString("Hello World!")
|
||
})
|
||
|
||
app.Get("/stats", func(c fiber.Ctx) error {
|
||
count, _ := c.App().State().Get("requestCount")
|
||
return c.SendString(fmt.Sprintf("Total requests: %d", count))
|
||
})
|
||
|
||
app.Listen(":3000")
|
||
}
|
||
```
|
||
|
||
### Example: Environment–Specific Configuration
|
||
|
||
This example shows how to configure different settings based on the environment.
|
||
|
||
```go
|
||
package main
|
||
|
||
import (
|
||
"os"
|
||
|
||
"github.com/gofiber/fiber/v3"
|
||
)
|
||
|
||
func main() {
|
||
app := fiber.New()
|
||
|
||
// Determine environment.
|
||
environment := os.Getenv("ENV")
|
||
if environment == "" {
|
||
environment = "development"
|
||
}
|
||
app.State().Set("environment", environment)
|
||
|
||
// Set environment-specific configurations.
|
||
if environment == "development" {
|
||
app.State().Set("apiUrl", "http://localhost:8080/api")
|
||
app.State().Set("debug", true)
|
||
} else {
|
||
app.State().Set("apiUrl", "https://api.production.com")
|
||
app.State().Set("debug", false)
|
||
}
|
||
|
||
app.Get("/config", func(c fiber.Ctx) error {
|
||
config := map[string]any{
|
||
"environment": environment,
|
||
"apiUrl": fiber.GetStateWithDefault(c.App().State(), "apiUrl", ""),
|
||
"debug": fiber.GetStateWithDefault(c.App().State(), "debug", false),
|
||
}
|
||
return c.JSON(config)
|
||
})
|
||
|
||
app.Listen(":3000")
|
||
}
|
||
```
|
||
|
||
### Example: Dependency Injection with State Management
|
||
|
||
This example demonstrates how to use the State for dependency injection in a Fiber application.
|
||
|
||
```go
|
||
package main
|
||
|
||
import (
|
||
"context"
|
||
"fmt"
|
||
"log"
|
||
|
||
"github.com/gofiber/fiber/v3"
|
||
"github.com/redis/go-redis/v9"
|
||
)
|
||
|
||
type User struct {
|
||
ID int `query:"id"`
|
||
Name string `query:"name"`
|
||
Email string `query:"email"`
|
||
}
|
||
|
||
func main() {
|
||
app := fiber.New()
|
||
ctx := context.Background()
|
||
|
||
// Initialize Redis client.
|
||
rdb := redis.NewClient(&redis.Options{
|
||
Addr: "localhost:6379",
|
||
Password: "",
|
||
DB: 0,
|
||
})
|
||
|
||
// Check the Redis connection.
|
||
if err := rdb.Ping(ctx).Err(); err != nil {
|
||
log.Fatalf("Could not connect to Redis: %v", err)
|
||
}
|
||
|
||
// Inject the Redis client into Fiber's State for dependency injection.
|
||
app.State().Set("redis", rdb)
|
||
|
||
app.Get("/user/create", func(c fiber.Ctx) error {
|
||
var user User
|
||
if err := c.Bind().Query(&user); err != nil {
|
||
return c.Status(fiber.StatusBadRequest).SendString(err.Error())
|
||
}
|
||
|
||
// Save the user to the database.
|
||
rdb, ok := fiber.GetState[*redis.Client](c.App().State(), "redis")
|
||
if !ok {
|
||
return c.Status(fiber.StatusInternalServerError).SendString("Redis client not found")
|
||
}
|
||
|
||
// Save the user to the database.
|
||
key := fmt.Sprintf("user:%d", user.ID)
|
||
err := rdb.HSet(ctx, key, "name", user.Name, "email", user.Email).Err()
|
||
if err != nil {
|
||
return c.Status(fiber.StatusInternalServerError).SendString(err.Error())
|
||
}
|
||
|
||
return c.JSON(user)
|
||
})
|
||
|
||
app.Get("/user/:id", func(c fiber.Ctx) error {
|
||
id := c.Params("id")
|
||
|
||
rdb, ok := fiber.GetState[*redis.Client](c.App().State(), "redis")
|
||
if !ok {
|
||
return c.Status(fiber.StatusInternalServerError).SendString("Redis client not found")
|
||
}
|
||
|
||
key := fmt.Sprintf("user:%s", id)
|
||
user, err := rdb.HGetAll(ctx, key).Result()
|
||
if err == redis.Nil {
|
||
return c.Status(fiber.StatusNotFound).SendString("User not found")
|
||
} else if err != nil {
|
||
return c.Status(fiber.StatusInternalServerError).SendString(err.Error())
|
||
}
|
||
|
||
return c.JSON(user)
|
||
})
|
||
|
||
app.Listen(":3000")
|
||
}
|
||
```
|