Skip to main content
The SessionStorage interface defines the contract that all session storage implementations must follow. This ensures consistent behavior across different database adapters.

Interface Definition

From the source code at packages/apps/session-storage/shopify-app-session-storage/src/types.ts:
import {Session} from '@shopify/shopify-api';

/**
 * Defines the strategy to be used to store sessions for the Shopify App.
 */
export interface SessionStorage {
  /**
   * Creates or updates the given session in storage.
   *
   * @param session Session to store
   */
  storeSession(session: Session): Promise<boolean>;

  /**
   * Loads a session from storage.
   *
   * @param id Id of the session to load
   */
  loadSession(id: string): Promise<Session | undefined>;

  /**
   * Deletes a session from storage.
   *
   * @param id Id of the session to delete
   */
  deleteSession(id: string): Promise<boolean>;

  /**
   * Deletes an array of sessions from storage.
   *
   * @param ids Array of session id's to delete
   */
  deleteSessions(ids: string[]): Promise<boolean>;

  /**
   * Return an array of sessions for a given shop (or [] if none found).
   *
   * @param shop shop of the session(s) to return
   */
  findSessionsByShop(shop: string): Promise<Session[]>;
}

Methods

storeSession

Creates or updates a session in storage.
session
Session
required
The session object to store. Contains all authentication and user data.
returns
Promise<boolean>
Returns true if the session was successfully stored, false otherwise.
Behavior:
  • If a session with the same id exists, it will be updated
  • If no session exists with that id, a new one will be created
  • This implements an “upsert” pattern (update or insert)
Example:
const session = new Session({
  id: 'offline_example.myshopify.com',
  shop: 'example.myshopify.com',
  state: 'state-value',
  isOnline: false,
  accessToken: 'shpat_xxxxx',
  scope: 'read_products,write_orders'
});

const success = await sessionStorage.storeSession(session);
if (success) {
  console.log('Session stored successfully');
}

loadSession

Retrieves a session from storage by its ID.
id
string
required
The unique identifier of the session to load.
returns
Promise<Session | undefined>
Returns the Session object if found, or undefined if no session exists with that ID.
Session ID Format:
  • Offline sessions: offline_{shop}
  • Online sessions: {shop}_{userId}
Example:
const session = await sessionStorage.loadSession(
  'offline_example.myshopify.com'
);

if (session) {
  console.log('Access token:', session.accessToken);
  console.log('Expires:', session.expires);
} else {
  console.log('Session not found');
}

deleteSession

Deletes a single session from storage.
id
string
required
The unique identifier of the session to delete.
returns
Promise<boolean>
Returns true if the session was successfully deleted, false otherwise.
Behavior:
  • Attempting to delete a non-existent session typically returns true
  • This method is idempotent (safe to call multiple times)
Example:
// Delete session when user logs out or uninstalls app
const deleted = await sessionStorage.deleteSession(
  'offline_example.myshopify.com'
);

if (deleted) {
  console.log('Session deleted');
}

deleteSessions

Deletes multiple sessions in a single operation.
ids
string[]
required
Array of session IDs to delete.
returns
Promise<boolean>
Returns true if all sessions were successfully deleted, false otherwise.
Use Cases:
  • Cleaning up all sessions for a shop when app is uninstalled
  • Batch deletion of expired sessions
  • Removing all online sessions for a shop
Example:
// Delete all sessions for a shop
const shopSessions = await sessionStorage.findSessionsByShop(
  'example.myshopify.com'
);

const sessionIds = shopSessions.map(session => session.id);
const deleted = await sessionStorage.deleteSessions(sessionIds);

if (deleted) {
  console.log(`Deleted ${sessionIds.length} sessions`);
}

findSessionsByShop

Finds all sessions associated with a specific shop.
shop
string
required
The shop domain (e.g., ‘example.myshopify.com’).
returns
Promise<Session[]>
Returns an array of Session objects. Returns an empty array if no sessions are found.
What It Returns:
  • Both online and offline sessions for the shop
  • Active and expired sessions (check expires field)
  • Sessions for all users who have accessed the app from that shop
Example:
// Find all sessions for a shop
const sessions = await sessionStorage.findSessionsByShop(
  'example.myshopify.com'
);

console.log(`Found ${sessions.length} sessions`);

sessions.forEach(session => {
  console.log(`- ${session.id} (online: ${session.isOnline})`);
});

// Filter to only active sessions
const now = new Date();
const activeSessions = sessions.filter(
  session => !session.expires || session.expires > now
);

Implementation Guidelines

If you’re implementing a custom session storage adapter, follow these guidelines:

Thread Safety

Implementations should be safe for concurrent access. Multiple requests may attempt to read/write sessions simultaneously.

Error Handling

Implementations should:
  • Catch database errors and log them appropriately
  • Return false on storage/deletion failures rather than throwing
  • Return undefined from loadSession on errors, not throw

Performance Considerations

  • Index the shop column for efficient findSessionsByShop queries
  • Use upsert operations in storeSession to avoid race conditions
  • Implement connection pooling for database adapters
  • Consider caching for frequently accessed sessions

Data Serialization

Different storage backends require different serialization:
Store each session property as a separate column:
// Convert Session to row data
const entries = session.toPropertyArray(true);

// Handle expires conversion (milliseconds to seconds)
if (row.expires) {
  row.expires = Math.floor(row.expires / 1000);
}

Deserialization

When loading sessions, reconstruct the Session object:
// From property array (for JSON storage)
const session = Session.fromPropertyArray(propertyArray, true);

// From object (for document storage)
const session = new Session(sessionObject);

Testing Your Implementation

The SDK provides testing utilities:
import {batteryOfTests} from '@shopify/shopify-app-session-storage-test-utils';
import {MyCustomSessionStorage} from './my-storage';

describe('MyCustomSessionStorage', () => {
  batteryOfTests(async () => new MyCustomSessionStorage());
});
The batteryOfTests function runs a comprehensive test suite that verifies your implementation correctly handles all SessionStorage methods.

Additional Interfaces

RdbmsSessionStorageOptions

Configuration for relational database adapters:
interface RdbmsSessionStorageOptions {
  sessionTableName: string;
  migratorOptions?: RdbmsSessionStorageMigratorOptions;
}
sessionTableName
string
default:"shopify_sessions"
Name of the table to store sessions.
migratorOptions
RdbmsSessionStorageMigratorOptions
Configuration for the migration system.

SessionStorageMigrator

Handles database migrations for session storage:
interface SessionStorageMigrator {
  initMigrationPersistence(): Promise<void>;
  hasMigrationBeenApplied(migrationName: string): Promise<boolean>;
  saveAppliedMigration(migrationName: string): Promise<void>;
  getMigrationList(): MigrationOperation[];
  applyMigrations(databaseReady: Promise<void>): Promise<void>;
}
Most users don’t need to interact with the migrator directly. It runs automatically when initializing RDBMS-based storage adapters.

Database Adapters

Setup guides for each official adapter

Session Storage Overview

Learn about session storage concepts

Build docs developers (and LLMs) love