Skip to main content

Swapping backends

The SDK's provider architecture makes the capability of swapping backends real; this guide covers the operational story.

Two scenarios come up in practice: runtime swap (flip providers without restarting the app) and migration (move memories from provider A to provider B).

Runtime swap

AtomicMemorySDK is initialized with a single default provider; swapping means destroying the current SDK and constructing a new one. There is no live reconfigure path, by design — providers hold HTTP clients, caches, and init state, and a clean reconstruction is easier to reason about than partial rewiring.

async function withProvider(providerName: 'atomicmemory' | 'mem0') {
const sdk = new AtomicMemorySDK({
userAccounts: { /* unchanged */ },
context: {
providers: {
default: providerName,
atomicmemory: { apiUrl: 'http://localhost:3050' },
mem0: { apiUrl: 'http://localhost:8000' },
},
},
});
await sdk.initialize();
return sdk;
}

// Somewhere in your app
const sdk = await withProvider('atomicmemory');
// ... use sdk ...
// Switch:
const sdk2 = await withProvider('mem0');

If you have an in-flight operation on the old SDK, let it complete before the swap — destroying the client mid-request is not a supported path.

Migration: moving memories between providers

Use list on the source, ingest on the target. Example:

async function migrate(
source: AtomicMemorySDK,
target: AtomicMemorySDK,
scope: { user: string },
) {
let cursor: string | undefined = undefined;
let migrated = 0;

do {
const page = await source.list({ scope, limit: 100, cursor });
for (const memory of page.memories) {
await target.ingest(
{
mode: 'memory',
memory,
scope,
provenance: { source: 'migration' },
},
'migration',
);
migrated += 1;
}
cursor = page.cursor;
} while (cursor);

console.log(`Migrated ${migrated} memories`);
}

Capability gaps are real

When source.capabilities().extensions declares features that target.capabilities().extensions does not, the migration is lossy along those dimensions:

Source hasTarget does notConsequence
Versioning (version history)Target holds only the latest version; history is dropped
Temporal (as-of timestamps)Target loses the temporal metadata; queries like searchAsOf will not work
Packaging (structured context)No immediate loss on migration; affects read-time behavior only
Custom extensionsAny data stored via customExtensions does not survive

Always run source.capabilities() and target.capabilities() before migrating and document which dimensions will not survive. A dry-run against a sample is cheap and informative.

Stateful caches and retries

If your migration job crashes, resume by finding the last successfully-migrated memory.id and starting source.list from there. The cursor returned by list is the natural checkpoint — persist it.

Next