README.md

# wisp_kv_sessions

[![Package Version](https://img.shields.io/hexpm/v/wisp_kv_sessions)](https://hex.pm/packages/wisp_kv_sessions)
[![Hex Docs](https://img.shields.io/badge/hex-docs-ffaff3)](https://hexdocs.pm/wisp_kv_sessions/)


# Overview
wisp_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/dynamic
import gleam/erlang/process
import gleam/json
import gleam/option
import gleam/result
import gleam/string_builder
import memory_store
import mist
import wisp
import wisp/wisp_mist
import wisp_kv_sessions
import wisp_kv_sessions/session
import wisp_kv_sessions/session_config

pub fn main() {
  // Setup session_store
  use memory_store <- result.map(memory_store.try_create_session_store())

  // Create session config
  let session_config =
    session_config.Config(
      default_expiry: session.ExpireIn(60 * 60),
      cookie_name: "SESSION_COOKIE",
      store: memory_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 {
  use req <- wisp_kv_sessions.middleware(session_config, req)

  case wisp.path_segments(req) {
    [] -> get_value_page(req, session_config)
    ["set"] -> set_value_page(req, session_config)
    _ -> wisp.not_found()
  }
}

fn get_value_page(
  req: wisp.Request,
  session_config: session_config.Config,
) -> wisp.Response {
  // Read value to the session
  let assert Ok(key) =
    wisp_kv_sessions.get(session_config, req, "test_key", dynamic.string)

  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,
      )
    }
  }
}

// Not nessesary for this simple type 
fn encode_string(str: String) {
  json.string(str) |> json.to_string
}

fn set_value_page(
  req: wisp.Request,
  session_config: session_config.Config,
) -> wisp.Response {
  // Set value to the session
  let _ =
    wisp_kv_sessions.set(
      session_config,
      req,
      "test_key",
      "something",
      encode_string,
    )
  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
```

# SessionStore Drivers

A SessionStore driver is a function that produces a SessionStore instance. These drivers allow for custom session storage solutions by applying methods to the SessionStore object, potentially using external resources like databases.

For an example implementation, see ./src/memory_store.gleam.

## Existing Drivers

### memory_store (Included by Default)
The memory_store 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
use memory_store <- result.map(memory_store.try_create_session_store())
```

### postgress_store (In Development)

A SessionStore driver for PostgreSQL is currently under development.