Session
Cookie-backed sessions with a pluggable store.
Session
The session package stores per-user values behind a cookie-backed session.
import "github.com/Dziqha/ply/session"
store := session.NewMemoryStore()
manager := session.NewManager(store)
app.Use(manager.Middleware())NewManager panics if the store is nil.
Read and write
app.Post("/login", func(c *core.Context) {
manager.Set(c, "user_id", "42")
c.JSON(200, map[string]string{"status": "ok"})
})
app.Get("/me", func(c *core.Context) {
userID, _ := manager.Get(c, "user_id").(string)
if userID == "" {
c.JSON(401, map[string]string{"error": "not signed in"})
return
}
c.JSON(200, map[string]string{"user_id": userID})
})| Method | Effect |
|---|---|
Set(c, key, value) | Stores a value in the current session |
Get(c, key) | Returns a value, or nil |
Delete(c, key) | Removes one key |
Destroy(c) | Deletes the session and expires the cookie |
Renew(c) | Generates a new session ID and keeps the values |
Call Renew after a successful login to prevent session fixation. The old ID
is deleted when the response is written.
Cookie defaults
| Setting | Default |
|---|---|
| Name | ply_session |
| Path | / |
| TTL | 24 hours |
Secure | true |
HttpOnly | true |
SameSite | Lax |
For local HTTP development, disable Secure:
manager.Secure = falseKeep HttpOnly enabled. JavaScript does not need access to the session cookie.
Set SameSite explicitly when the application needs cross-site requests:
manager.SameSite = fasthttp.CookieSameSiteNoneModeSameSite=None requires Secure=true in modern browsers.
Session ID
IDs are 32 random bytes encoded as 64 hexadecimal characters. They are
generated with crypto/rand.
The cookie contains only the session ID. Values stay in the store.
Memory store
NewMemoryStore keeps sessions in the current process. It is suitable for one
instance, local development, and tests.
Sessions disappear on restart and are not shared between instances. Use a shared store when running more than one process.
MemoryStore copies the top-level value map on save and load. Nested maps and
slices are shared, so treat stored values as immutable after Set.
Custom store
Implement three methods to use Redis, a database, or another backend:
type Store interface {
Load(id string) (map[string]any, error)
Save(id string, values map[string]any, ttl time.Duration) error
Delete(id string) error
}Return session.ErrNotFound when a session does not exist. Any other error
from Load produces a 500 response and aborts the request.
Errors from Save and Delete are reported through OnError:
manager.OnError = func(err error) {
logger.Error("session store", "error", err)
}When OnError is nil, store errors are ignored. Set it in production.
Expiration
The manager writes the TTL to the store and sets Max-Age on the cookie on
every response. The window is sliding: an active session stays alive, and an
idle session expires after 24 hours by default. The store must enforce the
TTL. MemoryStore checks expiration on load and removes expired entries
periodically.
The cookie and the stored session can expire at different times if a custom store does not honor the TTL.
Concurrency
MemoryStore is safe for concurrent use. A custom store must be safe for
concurrent requests.
A single session can be loaded by multiple requests at once. The last request to write wins. Avoid storing counters that must be updated atomically in a session; use a database or cache operation for those values.

Ply