npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@dev-swarup/elysia-session

v1.2.1

Published

![badge](https://github.com/dev-swarup/elysia-session/actions/workflows/npm-publish.yml/badge.svg)

Readme

Elysia Session

badge

Features

  • Runs in Bun, and those supported by Elysia v1+.
  • Flash messages — data that is deleted once it's read (one-off error messages, etc.)
  • Built-in Memory, Cookie, Bun SQLite and Redis stores.
  • Automatic eviction of expired sessions (MemoryStore).

Installation

bun add @dev-swarup/elysia-session

Documentation

There are 4 stores built into this package:

  1. Memory Store — in-process, great for development
  2. Cookie Store — stores the session directly inside the browser cookie (no server storage needed)
  3. Redis Store — production-ready, accepts an ioredis-compatible Redis instance
  4. Bun SQLite Store — lightweight persistent storage using bun:sqlite

You can implement your own store by implementing the Store interface as shown below:

import type { Context } from "elysia";
import type { Store } from "@dev-swarup/elysia-session/store";
import type { SessionData } from "@dev-swarup/elysia-session/session";

export class MyCustomStore implements Store {
  constructor() {
    // initialise your storage backend here
  }

  getSession(id?: string, ctx?: Context): SessionData | null | undefined | Promise<SessionData | null | undefined> {
    // retrieve session by id
  }

  createSession(data: SessionData, id?: string, ctx?: Context): Promise<void> | void {
    // persist a newly created session
  }

  persistSession(data: SessionData, id?: string, ctx?: Context): Promise<void> | void {
    // update an existing session (called after every request)
  }

  deleteSession(id?: string, ctx?: Context): Promise<void> | void {
    // remove a session from storage
  }
}

Usage

Memory Store

Sessions are stored in-process. All sessions are lost on server restart. An automatic background timer evicts expired sessions every 60 seconds by default.

import Elysia from "elysia";
import { sessionPlugin } from "@dev-swarup/elysia-session";
import { MemoryStore } from "@dev-swarup/elysia-session/stores/memory";

const store = new MemoryStore({ evictionIntervalMs: 60_000 }); // default

new Elysia()
  .use(sessionPlugin({
    cookieName: "session", // optional, defaults to "session"
    store,
    expireAfter: 15 * 60, // 15 minutes
  }))
  .get("/", () => "Hi")
  .listen(3000);

// On graceful shutdown, stop the eviction timer:
// store.destroy();

Cookie Store

The entire session is serialized as JSON and stored directly in the browser cookie. No server-side storage required. Cookie payload is limited to ~4 KB.

import Elysia from "elysia";
import { sessionPlugin } from "@dev-swarup/elysia-session";
import { CookieStore } from "@dev-swarup/elysia-session/stores/cookie";

new Elysia()
  .use(sessionPlugin({
    cookieName: "session", // optional, defaults to "session"
    store: new CookieStore({
      cookieName: "session",    // optional, defaults to "session"
      cookieOptions: {
        httpOnly: true,
        secure: true,
        sameSite: "lax",
      },
    }),
    expireAfter: 15 * 60, // 15 minutes
  }))
  .get("/", () => "Hi")
  .listen(3000);

Redis Store

import Elysia from "elysia";
import { sessionPlugin } from "@dev-swarup/elysia-session";
import { RedisStore } from "@dev-swarup/elysia-session/stores/redis";

new Elysia()
  .use(sessionPlugin({
    cookieName: "session", // optional, defaults to "session"
    store: new RedisStore("redis://localhost:6379", {
      keyPrefix: "sess:",
    }),
    expireAfter: 15 * 60, // 15 minutes
  }))
  .get("/", () => "Hi")
  .listen(3000);

You can also pass an existing ioredis client:

import { Redis } from "ioredis";
import { RedisStore } from "@dev-swarup/elysia-session/stores/redis";

const redis = new Redis({ host: "localhost", port: 6379 });
const store = new RedisStore({ client: redis });

Bun SQLite Store

import Elysia from "elysia";
import { Database } from "bun:sqlite";
import { sessionPlugin } from "@dev-swarup/elysia-session";
import { BunSQLiteStore } from "@dev-swarup/elysia-session/stores/sqlite";

const database = new Database(":memory:");
// 2nd argument is the SQL table name (letters, digits, underscores only)
const store = new BunSQLiteStore(database, "sessions");

new Elysia()
  .use(sessionPlugin({
    cookieName: "session", // optional, defaults to "session"
    store,
    expireAfter: 15 * 60, // 15 minutes
  }))
  .get("/", () => "Hi")
  .listen(3000);

CSRF Protection

Optional, disabled by default. Pass a csrf object to sessionPlugin to enable it — token generation/verification is powered by Bun's built-in Bun.CSRF API, so this only works when running under Bun.

Enabling it sets a readable CSRF cookie (double-submit-cookie pattern) on every response and rejects any request whose method isn't in safeMethods unless it carries a matching token in the configured header.

import Elysia from "elysia";
import { sessionPlugin } from "@dev-swarup/elysia-session";
import { MemoryStore } from "@dev-swarup/elysia-session/stores/memory";

new Elysia()
  .use(sessionPlugin({
    store: new MemoryStore(),
    expireAfter: 15 * 60,
    csrf: {
      cookieName: "csrf_token",   // optional, defaults to "csrf_token"
      headerName: "x-csrf-token", // optional, defaults to "x-csrf-token"
      safeMethods: ["GET", "HEAD", "OPTIONS"], // optional, this is the default
    },
  }))
  .post("/transfer", (ctx) => "Money moved!")
  .listen(3000);

On the client, read the csrf_token cookie and send its value back in the x-csrf-token header on state-changing requests (POST/PUT/PATCH/DELETE):

fetch("/transfer", {
  method: "POST",
  headers: { "x-csrf-token": getCookie("csrf_token") },
});

Requests to unsafe methods without a valid token receive a 403 response.

Community Stores

import type { Context } from "elysia";
import type { SessionData } from "@dev-swarup/elysia-session/session";
import type { Store } from "@dev-swarup/elysia-session/store";
import * as mongoose from 'mongoose';

export interface ISession extends mongoose.Document {
  _id: string;
  sessionData: SessionData;
}

export class MongooseStore implements Store {
  private db: typeof import('mongoose');
  private collection: string;
  private schema: mongoose.Schema | null;
  private model: mongoose.Model<ISession> | null;

  constructor(db: typeof import('mongoose'), collection: string) {
    this.db = db;
    this.collection = collection;
    this.schema = new mongoose.Schema({
      _id: String,
      sessionData: { type: JSON },
    })
    this.model = mongoose.model<ISession>(collection, this.schema);
  }

  getSession(id?: string, ctx?: Context): SessionData | Promise<SessionData | null | undefined> | null | undefined {
    if (!id) return null;
    if (this.model) {
      this.model.findOne({ _id: id }, (err: Error, session: ISession) => {
        if (err || !session) return null;
        return session.sessionData
      })
    } else
      return null
  }

  createSession(data: SessionData, id: string, ctx?: Context): void | Promise<void> {
    if (this.model) {
      const session = new this.model({ _id: id, sessionData: data })
      session.save();
    }
  }

  deleteSession(id?: string, ctx?: Context): void | Promise<void> {
    if (!id) return;
    if (this.model) {
      this.model.deleteOne({ _id: id })
    }
  }

  persistSession(data: SessionData, id?: string, ctx?: Context): Promise<void> | void {
    if (!id) return;
    if (this.model) {
      this.model.updateOne({ _id: id }, { sessionData: data })
    }
  }
}

Flash Messages

Flash messages are one-off messages that are automatically deleted the first time they are read. They are useful for passing one-time notifications (e.g. form validation errors) between redirects.

Use .flash(key, value) to set, and .get(key) to read and consume:

app.post("/login", (ctx) => {
  // validation failed — set a flash message and redirect
  ctx.session.flash("error", "Invalid username or password");
  return ctx.redirect("/login");
});

app.get("/login", (ctx) => {
  const error = ctx.session.get("error"); // consumed — will be null on next read
  return `<p>${error ?? ""}</p><form>...</form>`;
});

Deleting a Session

To destroy a session completely (e.g. on logout), call .delete(). The session data will be removed from the store and the browser cookie will be cleared after the current request handler completes.

app.post("/logout", (ctx) => {
  ctx.session.delete();
  return ctx.redirect("/login");
});

Session Data

import Elysia from "elysia";
import { sessionPlugin } from "@dev-swarup/elysia-session";
import { MemoryStore } from "@dev-swarup/elysia-session/stores/memory";

new Elysia()
  .use(sessionPlugin({
    store: new MemoryStore(),
    expireAfter: 15 * 60, // 15 minutes
  }))
  .get("/set", (ctx) => {
    ctx.session.set("user", { id: 1, name: "Alice" });
    return "Session set!";
  })
  .get("/get", (ctx) => {
    const user = ctx.session.get("user");
    return user ?? "No session found";
  })
  .listen(3000);

License

MIT

Author

Copyright (c) 2023 Gaurish Sethia, All rights reserved. Copyright (c) 2025-26 Swarup Banerjee, All rights reserved.