Express CSV Logo

Recovered sessions

sessionRecovery in your CSVImporter options persists unfinished imports for a later visit. By default ExpressCSV stores no importer state.

Recovery Fingerprint

Recovered sessions are stored against a combined fingerprint of two values:

  • importNamespace, the stable namespace string your app assigns to this importer configuration

  • the schema shape

  • ExpressCSV first uses importNamespace to decide whether a saved browser session belongs to this importer, then falls back to schema fingerprints when needed.

  • Keep importNamespace stable for the same workflow (for example product-import); use a different value for a genuinely different importer.

  • If importNamespace or the schema shape changes in an incompatible way, ExpressCSV may start a new session instead of recovering the old one.

Local Recovery

Use the built-in browser persistence backend:

import { CSVImporter, x } from "@expresscsv/sdk";

const importer = new CSVImporter({
  schema,
  getSessionToken: async () => fetchSessionToken(),
  // Stable namespace for this importer workflow. ExpressCSV uses this with your
  // schema shape to decide whether a saved session can be recovered.
  importNamespace: "product-import",
  // Persist unfinished imports in the browser for this namespace.
  sessionRecovery: { type: "local" }, 
});

This uses ExpressCSV's default browser persistence implementation and requires no extra code from your app.

Custom Recovery

Use type: "custom" when you want to persist recovery data in your own backend.

import {
  CSVImporter,
  type RecoveredSession,
  type RecoveredSessionData,
  type SessionRecoveryKey,
} from "@expresscsv/sdk";

const importer = new CSVImporter({
  schema,
  getSessionToken: async () => fetchSessionToken(),
  importNamespace: "product-import",
  sessionRecovery: { 
    type: "custom", 
    get: async (key: SessionRecoveryKey): Promise<RecoveredSession | null> => { 
      // Load a previously saved session for this recovery key.
      const response = await fetch(`/your-api/import-recovery/${encodeURIComponent(key)}`, {
        headers: {
          Authorization: `Bearer ${accessToken}`,
        },
      }); 
      if (!response.ok) return null; // No saved session, or your API chose not to return one.
      return (await response.json()) as RecoveredSession | null; 
    }, 
    set: async (key: SessionRecoveryKey, value: RecoveredSession) => { 
      // Persist in-progress row data and column mapping while the user works.
      await fetch(`/your-api/import-recovery/${encodeURIComponent(key)}`, { 
        method: "PUT", 
        headers: {
          "Content-Type": "application/json",
          Authorization: `Bearer ${accessToken}`,
        }, 
        body: JSON.stringify(value), 
      }); 
    }, 
    remove: async (key: SessionRecoveryKey) => { 
      // Clear recovery data after import completes or the user discards.
      await fetch(`/your-api/import-recovery/${encodeURIComponent(key)}`, { 
        method: "DELETE", 
        headers: {
          Authorization: `Bearer ${accessToken}`,
        }, 
      }); 
    }, 
  }, 
});

Adapter Contract

Your custom adapter only needs three methods:

// Read a saved session, or null if none exists for this key.
get(key: SessionRecoveryKey): Promise<RecoveredSession | null>;
// Upsert recovery state while the user works through the import.
set(key: SessionRecoveryKey, value: RecoveredSession): Promise<void>;
// Delete recovery state when the import finishes or the user discards.
remove(key: SessionRecoveryKey): Promise<void>;

RecoveredSessionData

type RecoveredSessionData = {
  rowData: Record<string, unknown>[]; // Parsed rows from the interrupted import
  columnKeys: string[]; // Source CSV column headers mapped in the importer
};

RecoveredSession

type RecoveredSession = {
  data: RecoveredSessionData; // Payload the SDK reads and writes via your adapter
};

Recovery Key

SessionRecoveryKey is an opaque string generated by the SDK. Treat it as a lookup key only.