Skip to main content

SessionCacheStrategy

SessionCacheStrategy

This strategy defines how sessions get cached. Since most requests will need the Session object for permissions data, it can become a bottleneck to go to the database and do a multi-join SQL query each time. Therefore, we cache the session data only perform the SQL query once and upon invalidation of the cache.

The Vendure default from v3.1+ is to use a the DefaultSessionCacheStrategy, which delegates to the configured CacheStrategy to store the session data. This should be suitable for most use-cases.

note

If you are using v3.1 or later, you should not normally need to implement a custom SessionCacheStrategy, since this is now handled by the DefaultSessionCacheStrategy.

Prior to v3.1, the default was to use the InMemorySessionCacheStrategy, which is fast but suitable for single-instance deployments.

info

This is configured via the authOptions.sessionCacheStrategy property of your VendureConfig.

Here's an example implementation using Redis. To use this, you need to add the ioredis package as a dependency.

Example

import { CachedSession, Logger, SessionCacheStrategy, VendurePlugin } from '@vendure/core';
import { Redis, RedisOptions } from 'ioredis';

export interface RedisSessionCachePluginOptions {
namespace?: string;
redisOptions?: RedisOptions;
}
const loggerCtx = 'RedisSessionCacheStrategy';
const DEFAULT_NAMESPACE = 'vendure-session-cache';
const DEFAULT_TTL = 86400;

export class RedisSessionCacheStrategy implements SessionCacheStrategy {
private client: Redis;
constructor(private options: RedisSessionCachePluginOptions) {}

init() {
this.client = new Redis(this.options.redisOptions as RedisOptions);
this.client.on('error', err => Logger.error(err.message, loggerCtx, err.stack));
}

async destroy() {
await this.client.quit();
}

async get(sessionToken: string): Promise<CachedSession | undefined> {
try {
const retrieved = await this.client.get(this.namespace(sessionToken));
if (retrieved) {
try {
return JSON.parse(retrieved);
} catch (e: any) {
Logger.error(`Could not parse cached session data: ${e.message}`, loggerCtx);
}
}
} catch (e: any) {
Logger.error(`Could not get cached session: ${e.message}`, loggerCtx);
}
}

async set(session: CachedSession) {
try {
await this.client.set(this.namespace(session.token), JSON.stringify(session), 'EX', DEFAULT_TTL);
} catch (e: any) {
Logger.error(`Could not set cached session: ${e.message}`, loggerCtx);
}
}

async delete(sessionToken: string) {
try {
await this.client.del(this.namespace(sessionToken));
} catch (e: any) {
Logger.error(`Could not delete cached session: ${e.message}`, loggerCtx);
}
}

clear() {
// not implemented
}

private namespace(key: string) {
return `${this.options.namespace ?? DEFAULT_NAMESPACE}:${key}`;
}
}

@VendurePlugin({
configuration: config => {
config.authOptions.sessionCacheStrategy = new RedisSessionCacheStrategy(
RedisSessionCachePlugin.options,
);
return config;
},
})
export class RedisSessionCachePlugin {
static options: RedisSessionCachePluginOptions;
static init(options: RedisSessionCachePluginOptions) {
this.options = options;
return this;
}
}
Signature
interface SessionCacheStrategy extends InjectableStrategy {
set(session: CachedSession): void | Promise<void>;
get(sessionToken: string): CachedSession | undefined | Promise<CachedSession | undefined>;
delete(sessionToken: string): void | Promise<void>;
clear(): void | Promise<void>;
}

set

method
(session: CachedSession) => void | Promise<void>

Store the session in the cache. When caching a session, the data should not be modified apart from performing any transforms needed to get it into a state to be stored, e.g. JSON.stringify().

get

method
(sessionToken: string) => CachedSession | undefined | Promise<CachedSession | undefined>

Retrieve the session from the cache

delete

method
(sessionToken: string) => void | Promise<void>

Delete a session from the cache

clear

method
() => void | Promise<void>

Clear the entire cache

CachedSessionUser

A simplified representation of the User associated with the current Session.

Signature
type CachedSessionUser = {
id: ID;
identifier: string;
verified: boolean;
channelPermissions: UserChannelPermissions[];
}

id

property

identifier

property
string

verified

property
boolean

channelPermissions

property
UserChannelPermissions[]

CachedSession

A simplified representation of a Session which is easy to store.

Signature
type CachedSession = {
cacheExpiry: number;
id: ID;
token: string;
expires: Date;
activeOrderId?: ID;
authenticationStrategy?: string;
user?: CachedSessionUser;
activeChannelId?: ID;
}

cacheExpiry

property
number

The timestamp after which this cache entry is considered stale and a fresh copy of the data will be set. Based on the sessionCacheTTL option.

id

property

token

property
string

expires

property
Date

activeOrderId

property

authenticationStrategy

property
string

user

activeChannelId

property