{"title":"Session Adapters","description":"Implement and register Amber V2 session storage adapters","section":"guides/adapters","version":"v2","path":"guides/adapters/sessions","canonical_url":"https://amberframework.org/docs/v2/guides/adapters/sessions","markdown_url":"https://amberframework.org/docs/v2/guides/adapters/sessions.md","inherited":false,"content_markdown":"# Session Adapters\n\nSession adapters store the key/value data associated with a session ID. Amber\nV2 includes `MemorySessionAdapter`; applications can register another backend\nthrough `AdapterFactory` when state must survive a restart or be shared across\nprocesses.\n\n## Complete adapter contract\n\nA custom adapter inherits `Amber::Adapters::SessionAdapter` and implements every\nabstract operation.\n\n**Reference API: implemented by a class under `src/adapters/`, for example\n`src/adapters/redis_session_adapter.cr`. Do not copy the abstract class into the\napplication.**\n\n```crystal\nabstract class Amber::Adapters::SessionAdapter\n  abstract def get(session_id : String, key : String) : String?\n  abstract def set(session_id : String, key : String, value : String) : Nil\n  abstract def delete(session_id : String, key : String) : Nil\n  abstract def destroy(session_id : String) : Nil\n  abstract def exists?(session_id : String, key : String) : Bool\n  abstract def keys(session_id : String) : Array(String)\n  abstract def values(session_id : String) : Array(String)\n  abstract def to_hash(session_id : String) : Hash(String, String)\n  abstract def empty?(session_id : String) : Bool\n  abstract def expire(session_id : String, seconds : Int32) : Nil\n  abstract def batch_set(session_id : String, values : Hash(String, String)) : Nil\n  abstract def batch(session_id : String, &block : Amber::Adapters::SessionBatchOperations ->) : Nil\nend\n```\n\nAdapters may also override `close` to release connections and `healthy?` to\nreport backend availability.\n\n`batch_set` and `batch` should be atomic when the backend supports transactions\nor pipelining. The expiration operation applies to the complete session, not an\nindividual key.\n\n## Built-in memory adapter\n\n**File: the applicable file under `config/environments/`, such as\n`config/environments/development.yml` — edit its existing `session:` section.**\n\n```yaml\nsession:\n  key: \"my_app.session\"\n  store: \"signed_cookie\"\n  adapter: \"memory\"\n  expires: 3600\n```\n\nMemory state belongs to one application process and disappears when that process\nstops. Use it for development, tests, or a deployment where that lifecycle is an\nexplicit product decision.\n\n## Register an application adapter\n\nLoad and register the adapter before Amber builds the configured session store.\nThe generated application entry point requires top-level `config/*`, including\n`config/application.cr`, before application source, so it is a reliable\nregistration point.\n\n**File: `config/application.cr` — keep `require \"amber\"`, require the adapter\nclass, then register it before routes are loaded.**\n\n```crystal\n# config/application.cr\nrequire \"amber\"\nrequire \"../src/adapters/redis_session_adapter\"\n\nAmber::Adapters::AdapterFactory.register_session_adapter(\"redis\") do\n  RedisSessionAdapter.new(redis_client)\nend\n```\n\n**File: `config/environments/production.yml` — edit the existing `session:`\nsection after the adapter is registered.**\n\n```yaml\n# config/environments/production.yml\nsession:\n  key: \"my_app.session\"\n  store: \"signed_cookie\"\n  adapter: \"redis\"\n  expires: 86400\n```\n\nThe generated V2 application does not automatically require every file under\n`config/initializers/`. If you choose that directory, add an explicit require\nbefore `Amber::Server.start` and prove the load order in a clean build.\n\n## Adapter verification\n\nTest the implementation independently from controller behavior:\n\n- create, read, update, and delete more than one key in a session;\n- distinguish deleting one key from destroying the complete session;\n- return consistent results from `keys`, `values`, `to_hash`, and `empty?`;\n- expire a session and verify its keys disappear;\n- prove `batch_set` and `batch` do not expose a partial update;\n- exercise backend timeout, reconnect, and unavailable states;\n- close connections cleanly during shutdown;\n- run concurrent access tests that match the deployment process model.\n\nFor a Redis migration, also preserve or intentionally replace the previous key\nnamespace, serialization, expiration, and active-session policy. See\n[Redis to Adapters](../../migration-guide/redis-to-adapters/)."}