Browse docs

SDK

Tap to expand

Contribute

SDKUpdated 2026-03-18

User and Session Scoping

Use db.user(id) and db.user(id).session(id) to scope all operations without repeating identifiers on every call.

Applies to: @retaindb/sdk

db.user(userId) creates a scoped handle for one user. All operations on that handle automatically use the right user identifier — you never repeat it.

The pattern

typescript
import { RetainDB } from "@retaindb/sdk";

const db = new RetainDB({ apiKey: process.env.RETAINDB_KEY });

// Everything is scoped to this user
const user = db.user("user-123");

await user.remember("Prefers bullet-point responses");
const { context } = await user.getContext("How to answer this user?");
await user.forget(memoryId);

Adding a session

session() scopes memories to a specific conversation or run:

typescript
const session = db.user("user-123").session("chat-456");

// Scoped to user-123 AND chat-456
await session.remember("User is asking about the billing issue");
const { context } = await session.getContext("What is the current issue?");

Use session() when:

  • you want to debug a specific conversation
  • you need memories isolated to one run
  • you are tracking what happened in a single thread

Use just user() when:

  • you want durable facts and preferences across all sessions
  • you are storing long-term profile information

Full runTurn with session

typescript
const { response } = await db.user(userId).session(sessionId).runTurn({
  messages,
  generate: (ctx) => openai.chat.completions.create({
    model: "gpt-4o",
    messages: ctx.messages,
  }),
});

What runTurn does

  1. Extracts the last user message to use as the search query
  2. Retrieves relevant memories for the user (and session if scoped)
  3. Injects memories as a system message in ctx.messages
  4. Calls your generate function with the enriched context
  5. Stores the full conversation in the background (non-blocking)

The storage step is fire-and-forget — it never delays your response.


What getContext returns

typescript
const { context, raw } = await db.user(userId).getContext("query");

// context — formatted string, ready to inject as system prompt
// raw     — full search response with scores, metadata, etc.

Conversation dumps

Pass the full message array to remember and RetainDB extracts facts, preferences, and events automatically:

typescript
await db.user(userId).remember([
  { role: "user", content: "I work at Acme Corp as an engineer" },
  { role: "assistant", content: "Got it! How can I help you today?" },
  { role: "user", content: "I prefer short answers" },
]);

Each turn is ingested and stored. Future getContext calls will surface the relevant parts.


One db per process

Create one RetainDB instance and reuse it. The internal write queue auto-drains on process exit — no manual flush needed in long-running servers.

typescript
// At the top of your app, once
export const db = new RetainDB({ apiKey: process.env.RETAINDB_KEY });

// In your handler
export async function handleMessage(userId: string, messages: Message[]) {
  return db.user(userId).runTurn({
    messages,
    generate: (ctx) => llm.generate(ctx),
  });
}

Next step

Was this page helpful?

Your feedback helps us prioritize docs improvements weekly.