# kv_sessions
[![Package Version](https://img.shields.io/hexpm/v/kv_sessions)](https://hex.pm/packages/kv_sessions)
[![Hex Docs](https://img.shields.io/badge/hex-docs-ffaff3)](https://hexdocs.pm/kv_sessions/)
# Overview
kv_sessions is a key-value session management library for [Wisp](https://gleam-wisp.github.io/wisp/), inspired by the Rust crate [tower sessions](https://docs.rs/tower-sessions/latest/tower_sessions/#). This library allows you to manage user sessions with ease, storing session data in a simple key-value store.
# Example Usage
An minimal example usage is available in `./example/app.gleam`.
```gleam
import gleam/erlang/process
import gleam/option
import gleam/result
import gleam/string_builder
import mist
import wisp
import wisp/wisp_mist
import kv_sessions
import kv_sessions/actor_adapter
import kv_sessions/session
import kv_sessions/session_config
pub fn main() {
// Setup session_adapter
use store <- result.map(actor_adapter.new())
use cache_store <- result.map(actor_adapter.new())
// Create session config
let session_config =
session_config.Config(
default_expiry: session.ExpireIn(60 * 60),
cookie_name: "SESSION_COOKIE",
store:,
cache: option.Some(cache_store),
)
let secret_key_base = wisp.random_string(64)
// Start the Mist web server.
let assert Ok(_) =
wisp_mist.handler(handle_request(_, session_config), secret_key_base)
|> mist.new
|> mist.port(8000)
|> mist.start_http
process.sleep_forever()
}
pub fn handle_request(req: wisp.Request, session_config) -> wisp.Response {
// Run the middleware and construct current_session
use req <- kv_sessions.middleware(req, session_config)
let current_session = kv_sessions.CurrentSession(req, session_config)
case wisp.path_segments(req) {
[] -> get_value_page(req, current_session)
["set"] -> set_value_page(req, current_session)
_ -> wisp.not_found()
}
}
fn get_value_page(
_req: wisp.Request,
session: kv_sessions.CurrentSession,
) -> wisp.Response {
// Read value to the session
let assert Ok(key) =
session
|> kv_sessions.key("test_key")
|> kv_sessions.get()
case key {
option.Some(k) -> {
wisp.html_response(string_builder.from_string(k), 200)
}
option.None -> {
wisp.html_response(
string_builder.from_string("No value set. Go to /set to set a value"),
200,
)
}
}
}
fn set_value_page(
_req: wisp.Request,
session: kv_sessions.CurrentSession,
) -> wisp.Response {
// Set value to the session
let _ =
session
|> kv_sessions.key("test_key")
|> kv_sessions.set("something")
wisp.redirect("/")
}
```
## Running the Example
To start the server, cd into /example and run `gleam run`.
Visit (http://localhost:8000) in your browser. The page should display "No value set. Go to /set to set a value."
Navigate to (http://localhost:8000/set). You will be redirected back to the main page, which will now display "something".
# Development
```sh
gleam run # Run the project
gleam test # Run the tests
just watch-test # Run test and reload on file changes
```
# Caching
A session config can be passed an optional cache parameter that is a `SessionStore`
wrapped in an `option.Option`. If the cache is `option.Some` sessions will be
fetched from the cache. If the data is not in the cache the `store` will be
tried.
Session data will be automatically added and removed from the cache.
# SessionStore
There are different places you may want to store sessions such as
postgres/ETS/sqlite. The way you integrate with them is through storage adapters
that implement the type SessionStore. You can use one of the prebuild storage
adapters from down below, or implement a new one if the one you
are looking for does not exist.
For an example implementation, see `./src/kv_sessions/ets_adapter.gleam`.
## SessionStore adapters
### actor_adapter
The actor_adapter driver is suitable for development and testing purposes,
but not recommended for production due to its non-concurrent nature.
Internally, it uses an [actor](https://hexdocs.pm/gleam_otp/gleam/otp/actor.html),
which may become a bottleneck under heavy loads.
*Usage Example:*
```gleam
import kv_sessions/actor_adapter
use session_store <- result.map(actor_adapter.new())
// ...
```
See `./example/src/app.gleam` for full example
### postgres_adapter
The postgres_adapter, that allows you to use postgres as
the storage implementation
```sh
gleam add kv_sessions_postgres_adapter
```
```gleam
import kv_sessions/postgres_adapter
let db =
pog.default_config()
|> pog.connect()
// Migrate
use _ <- result.try(postgres_adapter.migrate_up(conn))
// Setup session_store
use session_store <- result.map(postgres_adapter.new(conn))
//...
```
### ets_adapter
The ets_adapter uses [Erlang Term Storage](https://www.erlang.org/doc/apps/stdlib/ets.html)
and [carpenter](https://hexdocs.pm/carpenter/) to store session information.
*This will NOT be persistant after restarts*. But is a good option for caching.
```sh
gleam add kv_sessions_ets_adapter
```
```gleam
import kv_sessions/ets_adapter
// Setup session_store
use session_store <- result.map(ets_adapter.new(conn))
//...
```