Skip to main content
This guide covers session management, including creating, storing, and validating sessions.

Session Overview

A Session object stores authentication information for a shop or user:
interface Session {
  id: string;                    // Unique session identifier
  shop: string;                  // Shop domain (e.g., example.myshopify.com)
  state: string;                 // OAuth state parameter
  isOnline: boolean;            // Online (user) vs offline (shop) token
  scope?: string;               // Granted API scopes
  accessToken?: string;         // Access token for API requests
  expires?: Date;               // Token expiration (online tokens only)
  refreshToken?: string;        // Refresh token (if expiring tokens enabled)
  refreshTokenExpires?: Date;   // Refresh token expiration
  onlineAccessInfo?: {          // User info (online tokens only)
    associated_user: {
      id: number;
      first_name: string;
      last_name: string;
      email: string;
      account_owner: boolean;
      locale: string;
      collaborator: boolean;
      email_verified: boolean;
    };
  };
}
Source: lib/session/session.ts:149-188

Creating Sessions

Sessions are typically created during OAuth:
const {session} = await shopify.auth.callback({
  rawRequest: req,
  rawResponse: res,
});

// Session is now ready to use
console.log(session.id);          // 'offline_example.myshopify.com'
console.log(session.shop);        // 'example.myshopify.com'
console.log(session.accessToken); // 'shpat_xxxxx'

Manual Session Creation

You can also create sessions manually:
import {Session} from '@shopify/shopify-api';

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',
});
Source: lib/session/session.ts:190-192

Session Storage

Configure session storage when initializing the API:
import {shopifyApi} from '@shopify/shopify-api';
import {MemorySessionStorage} from '@shopify/shopify-app-session-storage-memory';

const shopify = shopifyApi({
  // ... other config
  sessionStorage: new MemorySessionStorage(),
});

Available Storage Adapters

  • @shopify/shopify-app-session-storage-memory - In-memory (development only)
  • @shopify/shopify-app-session-storage-postgresql - PostgreSQL
  • @shopify/shopify-app-session-storage-mysql - MySQL
  • @shopify/shopify-app-session-storage-mongodb - MongoDB
  • @shopify/shopify-app-session-storage-redis - Redis
  • @shopify/shopify-app-session-storage-sqlite - SQLite

Storing Sessions

Store sessions after OAuth:
app.get('/auth/callback', async (req, res) => {
  const {session} = await shopify.auth.callback({
    rawRequest: req,
    rawResponse: res,
  });

  // Store the session
  await shopify.config.sessionStorage.storeSession(session);
  
  res.redirect(`/?shop=${session.shop}`);
});

Loading Sessions

Load sessions by ID:
const sessionId = 'offline_example.myshopify.com';
const session = await shopify.config.sessionStorage.loadSession(sessionId);

if (!session) {
  throw new Error('Session not found');
}

Session ID Format

  • Offline tokens: offline_{shop}
  • Online tokens: {shop}_{userId}
// Offline session ID
const offlineId = `offline_${shop}`;

// Online session ID
const onlineId = `${shop}_${userId}`;

Validating Sessions

Check if Active

Validate that a session is still active:
const session = await shopify.config.sessionStorage.loadSession(sessionId);

if (!session.isActive(shopify.config.scopes)) {
  // Session is invalid - re-authenticate
  return res.redirect('/auth');
}

// Session is valid, proceed
A session is active when:
  1. It has an access token
  2. The token is not expired
  3. The granted scopes match required scopes
Source: lib/session/session.ts:198-206

Check Expiration

if (session.isExpired()) {
  console.log('Session is expired');
  // Refresh or re-authenticate
}
Source: lib/session/session.ts:235-240

Check Scopes

const hasScope = session.isScopeIncluded('write_products');

if (!hasScope) {
  console.log('Session missing required scope');
  // Re-authenticate with new scopes
}
Source: lib/session/session.ts:224-230

Deleting Sessions

Delete sessions when merchants uninstall:
// In APP_UNINSTALLED webhook handler
shopify.webhooks.addHandlers({
  APP_UNINSTALLED: {
    deliveryMethod: DeliveryMethod.Http,
    callbackUrl: '/webhooks/app-uninstalled',
    callback: async (topic, shop, body) => {
      const sessionId = `offline_${shop}`;
      await shopify.config.sessionStorage.deleteSession(sessionId);
      console.log(`Deleted session for ${shop}`);
    },
  },
});

Session Serialization

To Object

Convert a session to a plain object:
const sessionData = session.toObject();

console.log(sessionData);
// {
//   id: 'offline_example.myshopify.com',
//   shop: 'example.myshopify.com',
//   state: 'state',
//   isOnline: false,
//   scope: 'read_products',
//   accessToken: 'shpat_xxxxx'
// }
Source: lib/session/session.ts:245-272

To Property Array

Convert to key-value pairs for database storage:
const properties = session.toPropertyArray();

console.log(properties);
// [
//   ['id', 'offline_example.myshopify.com'],
//   ['shop', 'example.myshopify.com'],
//   ['isOnline', false],
//   ['accessToken', 'shpat_xxxxx'],
//   // ...
// ]
Source: lib/session/session.ts:300-341

From Property Array

Reconstruct a session from property array:
const entries = [
  ['id', 'offline_example.myshopify.com'],
  ['shop', 'example.myshopify.com'],
  ['isOnline', false],
  ['accessToken', 'shpat_xxxxx'],
];

const session = Session.fromPropertyArray(entries);
Source: lib/session/session.ts:25-147

Online vs Offline Sessions

Offline sessions represent the shop’s long-term access.
// Create offline session during OAuth
await shopify.auth.begin({
  shop,
  callbackPath: '/auth/callback',
  isOnline: false,
  rawRequest: req,
  rawResponse: res,
});
Characteristics:
  • Tied to the shop, not a user
  • No expiration date
  • Persist until app is uninstalled
  • Used for background jobs, webhooks
Session ID format: offline_{shop}

Session Comparison

Compare two sessions:
const session1 = await shopify.config.sessionStorage.loadSession(id1);
const session2 = await shopify.config.sessionStorage.loadSession(id2);

if (session1.equals(session2)) {
  console.log('Sessions are identical');
}
Source: lib/session/session.ts:277-295

Decoding Session Tokens

For embedded apps, decode session tokens from requests:
const sessionToken = req.headers['authorization']?.replace('Bearer ', '');

const payload = await shopify.session.decodeSessionToken(sessionToken);

console.log('Shop:', payload.dest);
console.log('User ID:', payload.sub);

Error Handling

import {InvalidSession, SessionStorageError} from '@shopify/shopify-api';

try {
  const session = await shopify.config.sessionStorage.loadSession(id);
} catch (error) {
  if (error instanceof SessionStorageError) {
    console.error('Storage error:', error.message);
  } else if (error instanceof InvalidSession) {
    console.error('Invalid session data:', error.message);
  }
}
Source: lib/error.ts:97-113

Complete Example

import {shopifyApi} from '@shopify/shopify-api';
import {PostgreSQLSessionStorage} from '@shopify/shopify-app-session-storage-postgresql';

const shopify = shopifyApi({
  apiKey: process.env.SHOPIFY_API_KEY,
  apiSecretKey: process.env.SHOPIFY_API_SECRET,
  scopes: ['read_products', 'write_orders'],
  hostName: process.env.HOST,
  sessionStorage: new PostgreSQLSessionStorage(
    process.env.DATABASE_URL
  ),
});

Best Practices

  • Always validate sessions before API calls
  • Use offline tokens for background jobs
  • Use online tokens for user-specific operations
  • Store both online and offline sessions
  • Delete sessions on app uninstall
  • Implement session refresh for expiring tokens
  • Use production-ready storage (PostgreSQL, MySQL, Redis)
  • Never store sessions in memory in production
Memory session storage is only for development. It doesn’t persist across restarts and doesn’t scale. Always use a database adapter in production.

Build docs developers (and LLMs) love