Fresh Session 🍋

Dead simple cookie-based session for Deno Fresh.

Get started

Fresh Session comes with a simple middleware to add at the root of your project, which will create or resolve a session from the request cookie.

Install / Import

You can import Fresh Session like so:

import {
  cookieSession,
  CookieSessionStorage,
  createCookieSessionStorage,
  Session,
  WithSession,
} from "https://deno.land/x/fresh_session@0.2.0/mod.ts";

Setup secret key

Fresh Session currently uses JSON Web Token under the hood to create and manage session in the cookies.

JWT requires a secret key to sign new tokens. Fresh Session uses the secret key from your environment variable APP_KEY.

If you don’t know how to setup environment variable locally, I wrote an article about .env file in Deno Fresh.

Create a root middleware (./routes/_middleware.ts)

import { MiddlewareHandlerContext } from "$fresh/server.ts";
import { cookieSession, WithSession } from "fresh-session";

export type State = {} & WithSession;

const session = cookieSession();

function sessionHandler(req: Request, ctx: MiddlewareHandlerContext<State>) {
  return session(req, ctx);
}
export const handler = [sessionHandler];

Learn more about Fresh route middleware.

Interact with the session in your routes

Now that the middleware is setup, it’s going to handle creating/resolving session based on the request cookie. So all that you need to worry about is interacting with your session.

// ./routes/dashboard.tsx
import { Handlers, PageProps } from "$fresh/server.ts";
import { WithSession } from "https://deno.land/x/fresh_session@0.2.0/mod.ts";

export type Data = { session: Record<string, string> };

export const handler: Handlers<
  Data,
  WithSession // indicate with Typescript that the session is in the `ctx.state`
> = {
  GET(_req, ctx) {
    // The session is accessible via the `ctx.state`
    const { session } = ctx.state;

    // Access data stored in the session
    session.get("email");
    // Set new value in the session
    session.set("email", "hello@deno.dev");
    // returns `true` if the session has a value with a specific key, else `false`
    session.has("email");
    // clear all the session data
    session.clear();
    // Access all session data value as an object
    session.data;
    // Add flash data which will disappear after accessing it
    session.flash("success", "Successfully flashed a message!");
    // Accessing the flashed data
    // /!\ This flashed data will disappear after accessing it one time.
    session.flash("success");
    // Session Key Rotation only kv store and redis store.
    // Is not work in cookie store.

    // Rotate the session key. Only supported by the kv store and redis store, not the cookie store.
    // If using the session for authentication, with a kv or redis store, it is recommended to rotate the key at login to prevent session fixation attack.
    // The cookie store is immune from this issue.
    session.keyRotate();

    return ctx.render({
      session: session.data, // You can pass the whole session data to the page
    });
  },
};

export default function Dashboard({ data }: PageProps<Data>) {
  return <div>You are logged in as {data.session.email}</div>;
}

session value is cookie. can set the option for cookie.

import { cookieSession } from "fresh-session";

export const handler = [
  cookieSession({
    maxAge: 30, //Session keep is 30 seconds.
    httpOnly: true,
  }),
];

In addition to JWT, values can be stored in Redis.

import { redisSession } from "fresh-session/mod.ts";
import { connect } from "redis/mod.ts";

const redis = await connect({
  hostname: "something redis server",
  port: 6379,
});

export const handler = [redisSession(redis)];

// or Customizable cookie options and Redis key prefix

export const handler = [
  redisSession(redis, {
    keyPrefix: "S_",
    maxAge: 10,
  }),
];

FAQ & Troubleshooting Errors

Some common questions and troubleshooting errors.

“TypeError: Headers are immutable.”

If you are receiving this error, you are likely using a Response.redirect, which makes the headers immutable. A workaround for this is to use the following instead:

new Response(null, {
  status: 302,
  headers: {
    Location: "your-url",
  },
});

Credit

Initial work done by @xstevenyung

Inspiration taken from Oak Sessions & thanks to @jcs224 for all the insight!