Session

The Session class in Harpia provides a simple, secure way to manage stateful data across multiple HTTP requests from the same client. It handles storing the session data on the server and synchronizing the session ID via cookies.

Initialization

To use sessions, you need to instantiate the Session class. By default, it uses an in-memory store and sets the cookie name to session_id.

import harpia, { Session } from "@harpiats/core";

const app = harpia();
const session = new Session();

Options

You can customize the store and the cookie name by passing an options object:

import { RedisStore } from "./my-custom-store";

const session = new Session({
  cookieName: "myapp_session",
  store: new RedisStore() // Must implement the Store interface
});

For a detailed guide on how to implement custom storage engines, please refer to the Stores documentation.

Creating a Session

When a user logs in or performs an action that requires a session, you can create one using the create method. This method generates a unique UUID, stores the data, and returns the session ID.

You must then manually set the cookie in the response using setCookie.

app.post("/login", async (req, res) => {
  const userData = { id: 1, name: "John Doe", role: "admin" };
  
  // 1. Create the session in the store
  const sessionId = await session.create(userData);
  
  // 2. Set the session ID in the client's cookie
  session.setCookie(res, sessionId, {
    httpOnly: true,
    secure: true,
    maxAge: 86400 // 1 day
  });
  
  res.json({ message: "Logged in successfully" });
});

Retrieving Session Data

To access session data in subsequent requests, you can use the fromRequest helper method. It automatically reads the cookie from the Request object and fetches the data from the store.

app.get("/profile", async (req, res) => {
  const userData = await session.fromRequest(req);
  
  if (!userData) {
    return res.status(401).send("Unauthorized");
  }
  
  res.json({ profile: userData });
});

WebSockets

You can also retrieve session data directly from a WebSocket connection using fromWebSocket:

app.ws("/chat", {
  async open(ws) {
    const userData = await session.fromWebSocket(ws);
    if (!userData) {
      ws.close();
      return;
    }
    console.log(`${userData.username} connected`);
  }
});

Updating a Session

If you need to add or modify data in an existing session (e.g., updating user preferences), use the update method. It merges the new data with the existing data.

app.patch("/preferences", async (req, res) => {
  const sessionId = req.cookies.get("session_id");
  
  if (sessionId) {
    const newPrefs = await req.json();
    await session.update(sessionId, { preferences: newPrefs });
    res.send("Preferences updated");
  }
});

Deleting a Session

When a user logs out, you should destroy the session. The delete method removes the data from the server-side store and also clears the cookie from the client’s browser.

app.post("/logout", async (req, res) => {
  const sessionId = req.cookies.get("session_id");
  
  if (sessionId) {
    // Deletes from store AND tells the browser to clear the cookie
    await session.delete(sessionId, res);
  }
  
  res.send("Logged out");
});

Memory Store

By default, Harpia uses MemoryStore. While this is great for development, it will reset every time you restart the server and does not share state across multiple server instances. For production, you should implement and inject a persistent store like Redis or a database.