TypeScript/Node.js SDK

The official TypeScript SDK for Recall provides full type safety and modern JavaScript features for Node.js applications.

Installation

bash
npm install @recall/client

Requirements

  • Node.js 16.0 or higher
  • TypeScript 4.5+ (optional but recommended)
  • Redis 6.0+ (local or cloud)
  • Mem0 API key

Quick Start

typescript
import { RecallClient } from "@recall/client";
// Initialize the client
const client = new RecallClient({
redisUrl: "redis://localhost:6379",
mem0ApiKey: "your-api-key",
});
// Store a memory
const memory = await client.add({
content: "User prefers TypeScript for web development",
userId: "user_123",
priority: "high",
});
// Search memories
const results = await client.search({
query: "programming preferences",
userId: "user_123",
});

Configuration

Environment Variables

Create a .env file:

env
RECALL_REDIS_URL=redis://localhost:6379
RECALL_MEM0_API_KEY=your-api-key
RECALL_ENVIRONMENT=production
RECALL_LOG_LEVEL=info

Load with dotenv:

typescript
import { RecallClient } from "@recall/client";
import dotenv from "dotenv";
dotenv.config();
const client = new RecallClient({
redisUrl: process.env.RECALL_REDIS_URL,
mem0ApiKey: process.env.RECALL_MEM0_API_KEY,
environment: process.env.RECALL_ENVIRONMENT,
});

Configuration Object

typescript
import { RecallClient, RecallConfig } from "@recall/client";
const config: RecallConfig = {
redis: {
url: "redis://localhost:6379",
options: {
maxRetries: 3,
retryDelay: 1000,
connectionTimeout: 5000,
},
},
mem0: {
apiKey: "your-api-key",
baseUrl: "https://api.mem0.ai",
timeout: 30000,
},
cache: {
ttl: 3600,
maxSize: 1000,
compression: true,
},
sync: {
mode: "lazy",
batchSize: 100,
interval: 60000,
},
};
const client = new RecallClient(config);

Type Definitions

Core Types

typescript
import {
Memory,
Priority,
User,
SearchResult,
CacheStats,
} from "@recall/client";
// Memory interface
interface Memory {
id: string;
content: string;
userId: string;
priority: Priority;
metadata?: Record<string, any>;
createdAt: Date;
updatedAt?: Date;
accessCount?: number;
}
// Priority enum
enum Priority {
CRITICAL = "critical",
HIGH = "high",
MEDIUM = "medium",
LOW = "low",
}
// Search result with relevance
interface SearchResult extends Memory {
score: number;
source: "cache" | "cloud";
highlights?: string[];
}

Method Signatures

typescript
class RecallClient {
// Add a memory
add(options: AddMemoryOptions): Promise<Memory>;
// Search memories
search(options: SearchOptions): Promise<SearchResult[]>;
// Get specific memory
get(memoryId: string): Promise<Memory | null>;
// Update memory
update(options: UpdateOptions): Promise<Memory>;
// Delete memory
delete(memoryId: string): Promise<boolean>;
// Batch operations
addBatch(memories: AddMemoryOptions[]): Promise<Memory[]>;
deleteBatch(memoryIds: string[]): Promise<BatchResult>;
}

Advanced Features

Async Iteration

typescript
import { RecallClient } from "@recall/client";
const client = new RecallClient();
// Iterate through search results
for await (const memory of client.searchIterator({
query: "user preferences",
userId: "user_123",
batchSize: 10,
})) {
console.log(`Processing: ${memory.content}`);
// Process each memory
}
// Stream all memories
const stream = client.streamAll({
userId: "user_123",
batchSize: 50,
});
for await (const batch of stream) {
console.log(`Batch of ${batch.length} memories`);
// Process batch
}

Event Emitters

typescript
import { RecallClient } from "@recall/client";
const client = new RecallClient();
// Listen to events
client.on("memory:added", (memory: Memory) => {
console.log(`New memory: ${memory.id}`);
});
client.on("cache:hit", (key: string) => {
console.log(`Cache hit for: ${key}`);
});
client.on("cache:miss", (key: string) => {
console.log(`Cache miss for: ${key}`);
});
client.on("sync:complete", (stats: SyncStats) => {
console.log(`Synced ${stats.count} memories`);
});
client.on("error", (error: Error) => {
console.error(`Error: ${error.message}`);
});

Middleware

typescript
import { RecallClient, Middleware } from "@recall/client";
// Custom middleware
class LoggingMiddleware implements Middleware {
async process(
operation: string,
params: any,
next: () => Promise<any>,
): Promise<any> {
console.log(`[${operation}] Starting...`);
const startTime = Date.now();
try {
const result = await next();
const duration = Date.now() - startTime;
console.log(`[${operation}] Completed in ${duration}ms`);
return result;
} catch (error) {
console.error(`[${operation}] Failed:`, error);
throw error;
}
}
}
// Apply middleware
const client = new RecallClient();
client.use(new LoggingMiddleware());
// Built-in middleware
import {
RetryMiddleware,
RateLimitMiddleware,
CacheMiddleware,
CompressionMiddleware,
} from "@recall/client/middleware";
client.use(new RetryMiddleware({ maxAttempts: 3 }));
client.use(new RateLimitMiddleware({ rps: 100 }));
client.use(new CompressionMiddleware());

Transactions

typescript
import { RecallClient } from "@recall/client";
const client = new RecallClient();
// Execute operations in a transaction
const result = await client.transaction(async (tx) => {
// All operations are atomic
const mem1 = await tx.add({
content: "First memory",
userId: "user_123",
});
const mem2 = await tx.add({
content: "Second memory",
userId: "user_123",
});
await tx.update({
memoryId: mem1.id,
priority: "critical",
});
return [mem1, mem2];
});
// Transaction with rollback
try {
await client.transaction(async (tx) => {
await tx.add({ content: "Memory 1", userId: "user_123" });
throw new Error("Rollback!");
// All changes rolled back
});
} catch (error) {
console.log("Transaction rolled back");
}

React Integration

React Hook

typescript
import { useRecall } from '@recall/react';
function UserPreferences({ userId }: { userId: string }) {
const {
memories,
loading,
error,
addMemory,
searchMemories,
refreshMemories
} = useRecall(userId);
const handleSearch = async (query: string) => {
const results = await searchMemories(query);
console.log(`Found ${results.length} memories`);
};
const handleAdd = async (content: string) => {
await addMemory({
content,
priority: 'high'
});
};
if (loading) return <div>Loading memories...</div>;
if (error) return <div>Error: {error.message}</div>;
return (
<div>
<h2>User Memories ({memories.length})</h2>
{memories.map(memory => (
<div key={memory.id}>{memory.content}</div>
))}
</div>
);
}

React Context Provider

typescript
import { RecallProvider } from '@recall/react';
import { RecallClient } from '@recall/client';
const client = new RecallClient({
redisUrl: process.env.NEXT_PUBLIC_REDIS_URL,
mem0ApiKey: process.env.NEXT_PUBLIC_MEM0_KEY
});
function App() {
return (
<RecallProvider client={client}>
<YourComponents />
</RecallProvider>
);
}
// Use in components
import { useRecallClient } from '@recall/react';
function Component() {
const client = useRecallClient();
const handleAction = async () => {
await client.add({
content: 'User action',
userId: 'user_123'
});
};
}

Express Middleware

typescript
import express from "express";
import { recallMiddleware } from "@recall/express";
const app = express();
// Add Recall to all requests
app.use(
recallMiddleware({
redisUrl: "redis://localhost:6379",
mem0ApiKey: "your-api-key",
}),
);
// Access in routes
app.get("/memories/:userId", async (req, res) => {
const { recall } = req;
const memories = await recall.getAll({
userId: req.params.userId,
});
res.json(memories);
});
app.post("/memories", async (req, res) => {
const { recall } = req;
const memory = await recall.add({
content: req.body.content,
userId: req.body.userId,
});
res.json(memory);
});

Testing

Mock Client

typescript
import { MockRecallClient } from "@recall/client/testing";
import { describe, it, expect, beforeEach } from "@jest/globals";
describe("Memory Service", () => {
let client: MockRecallClient;
beforeEach(() => {
client = new MockRecallClient();
});
it("should store memories", async () => {
const memory = await client.add({
content: "Test memory",
userId: "test_user",
});
expect(memory.id).toMatch(/^mem_/);
expect(client.getCallCount("add")).toBe(1);
expect(client.getLastCall("add")).toMatchObject({
content: "Test memory",
});
});
it("should search memories", async () => {
// Pre-populate test data
client.setSearchResults([
{ content: "Result 1", score: 0.9 },
{ content: "Result 2", score: 0.8 },
]);
const results = await client.search({
query: "test",
userId: "test_user",
});
expect(results).toHaveLength(2);
expect(results[0].score).toBe(0.9);
});
});

Test Utilities

typescript
import {
createTestMemory,
createTestUser,
populateTestData,
cleanupTestData,
} from "@recall/client/testing";
// Create test fixtures
const memory = createTestMemory({
content: "Test content",
priority: "high",
});
const user = createTestUser({
id: "test_user",
memories: 10,
});
// Populate database with test data
await populateTestData(client, {
users: 5,
memoriesPerUser: 20,
});
// Cleanup after tests
await cleanupTestData(client, "test_*");

Error Handling

Error Types

typescript
import {
RecallError,
ConnectionError,
AuthenticationError,
ValidationError,
RateLimitError,
TimeoutError,
} from "@recall/client/errors";
try {
await client.add({
content: "",
userId: "",
});
} catch (error) {
if (error instanceof ValidationError) {
console.error(`Validation failed: ${error.field} - ${error.message}`);
} else if (error instanceof ConnectionError) {
console.error(`Connection issue: ${error.service}`);
} else if (error instanceof RateLimitError) {
console.error(`Rate limited. Retry after: ${error.retryAfter}s`);
} else if (error instanceof RecallError) {
console.error(`Recall error: ${error.message}`);
}
}

Error Recovery

typescript
import { withRetry, withFallback } from "@recall/client/resilience";
// Automatic retry
const resilientClient = withRetry(client, {
maxAttempts: 3,
backoff: "exponential",
retryOn: [ConnectionError, TimeoutError],
});
// Fallback to cache on error
const fallbackClient = withFallback(client, {
fallbackTo: "cache",
on: [ConnectionError],
});
// Circuit breaker pattern
import { CircuitBreaker } from "@recall/client/resilience";
const breaker = new CircuitBreaker(client, {
threshold: 5,
timeout: 60000,
fallback: async () => {
return { source: "fallback", memories: [] };
},
});

Performance

Connection Pooling

typescript
import { RecallClient } from "@recall/client";
import Redis from "ioredis";
// Share Redis connection
const redis = new Redis({
host: "localhost",
port: 6379,
maxRetriesPerRequest: 3,
lazyConnect: true,
});
const client1 = new RecallClient({ redis });
const client2 = new RecallClient({ redis });

Caching Strategies

typescript
import { RecallClient, CacheStrategy } from "@recall/client";
const client = new RecallClient({
cacheStrategy: CacheStrategy.WRITE_THROUGH,
// or WRITE_BEHIND, CACHE_ASIDE, READ_THROUGH
cacheConfig: {
ttl: {
critical: null, // Never expire
high: 86400, // 24 hours
medium: 3600, // 1 hour
low: 300, // 5 minutes
},
warmup: true, // Pre-load frequently accessed
compression: true, // Compress large values
},
});

Batch Processing

typescript
import { RecallClient } from "@recall/client";
import { chunk } from "lodash";
const client = new RecallClient();
// Process large dataset in batches
const memories = generateLargeDataset(10000);
const batches = chunk(memories, 100);
for (const batch of batches) {
await client.addBatch(batch);
console.log(`Processed batch of ${batch.length}`);
}
// Parallel processing
const results = await Promise.all(
batches.map((batch) => client.addBatch(batch)),
);

CLI Usage

bash
# Install CLI globally
npm install -g @recall/cli
# Configure
recall config set redis.url redis://localhost:6379
recall config set mem0.apiKey your-api-key
# Commands
recall add "Memory content" --user user_123 --priority high
recall search "query" --user user_123 --limit 10
recall get mem_abc123
recall delete mem_abc123
recall stats
recall cache clear
recall sync

Deployment

Docker

dockerfile
FROM node:18-alpine
WORKDIR /app
# Copy package files
COPY package*.json ./
RUN npm ci --production
# Copy application
COPY . .
# Build TypeScript
RUN npm run build
# Health check
HEALTHCHECK --interval=30s --timeout=3s \
CMD node -e "require('@recall/client').RecallClient().healthCheck()"
CMD ["node", "dist/index.js"]

Environment-Specific Config

typescript
import { RecallClient } from "@recall/client";
const config = {
development: {
redisUrl: "redis://localhost:6379",
logLevel: "debug",
cache: { ttl: 300 },
},
production: {
redisUrl: process.env.REDIS_URL,
logLevel: "error",
cache: { ttl: 3600 },
},
};
const environment = process.env.NODE_ENV || "development";
const client = new RecallClient(config[environment]);

Migration Guide

From JavaScript to TypeScript

typescript
// Before (JavaScript)
const client = new RecallClient({
redisUrl: "redis://localhost:6379",
});
// After (TypeScript)
import { RecallClient, RecallConfig } from "@recall/client";
const config: RecallConfig = {
redis: { url: "redis://localhost:6379" },
mem0: { apiKey: "your-api-key" },
};
const client = new RecallClient(config);

From Callbacks to Promises

typescript
// Before (Callbacks)
client.add(memory, (err, result) => {
if (err) console.error(err);
else console.log(result);
});
// After (Promises)
try {
const result = await client.add(memory);
console.log(result);
} catch (error) {
console.error(error);
}

Next Steps