Integrations

Browse docs

Integrations

Tap to expand

Contribute

IntegrationsUpdated 2026-03-18

Next.js Integration

Complete guide to integrating RetainDB into Next.js applications with App Router and Pages Router.

Applies to: Next.js 13+

Learn how to integrate RetainDB into your Next.js application for production use.


Installation

bash
npm install @retaindb/sdk
# or
pnpm add @retaindb/sdk

App Router (Next.js 13+)

For maximum security, always Use RetainDB on the server:

typescript
// app/api/chat/route.ts
import { RetainDBClient } from "@retaindb/sdk";
import { NextResponse } from "next/server";

const client = new RetainDBClient({
  apiKey: process.env.RETAINDB_API_KEY!,
});

export async function POST(request: Request) {
  const { userId, message } = await request.json();

  // Search for relevant context
  const context = await client.memory.search({
    user_id: userId,
    query: message,
    top_k: 5,
  });

  // Generate response with your LLM
  const response = await generateResponse(message, context.results);

  // Store the interaction
  await client.memory.add({
    user_id: userId,
    content: `User: ${message}\nAssistant: ${response}`,
    memory_type: "event",
  });

  return NextResponse.json({ response });
}

Client Component with API Route

Never expose API keys in client components:

typescript
// app/page.tsx (Client Component)
"use client";

import { useState } from "react";

export default function Chat() {
  const [message, setMessage] = useState("");
  const [response, setResponse] = useState("");

  const sendMessage = async () => {
    const res = await fetch("/api/chat", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ userId: "user-123", message }),
    });
    const data = await res.json();
    setResponse(data.response);
  };

  return (
    <div>
      <input value={message} onChange={(e) => setMessage(e.target.value)} />
      <button onClick={sendMessage}>Send</button>
      <p>{response}</p>
    </div>
  );
}

RetainDB Hook

Create a reusable hook for your components:

typescript
// hooks/useRetainDB.ts
"use client";

import { useState, useCallback } from "react";

export function useRetainDB(userId: string) {
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState<string | null>(null);

  const search = useCallback(async (query: string) => {
    setLoading(true);
    setError(null);
    
    try {
      const res = await fetch("/api/RetainDB/search", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ userId, query }),
      });
      
      if (!res.ok) throw new Error("Search failed");
      
      return await res.json();
    } catch (e) {
      setError(e.message);
      return null;
    } finally {
      setLoading(false);
    }
  }, [userId]);

  const addMemory = useCallback(async (content: string, memoryType = "context") => {
    setLoading(true);
    setError(null);
    
    try {
      const res = await fetch("/api/RetainDB/add", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ userId, content, memoryType }),
      });
      
      if (!res.ok) throw new Error("Add failed");
      
      return await res.json();
    } catch (e) {
      setError(e.message);
      return null;
    } finally {
      setLoading(false);
    }
  }, [userId]);

  return { search, addMemory, loading, error };
}

Using the Hook

typescript
// app/components/ChatInput.tsx
"use client";

import { useRetainDB } from "@/hooks/useRetainDB";

export function ChatInput() {
  const { search, addMemory, loading } = useRetainDB("user-123");

  const handleSubmit = async (query: string) => {
    // Search existing context
    const context = await search(query);
    
    // Add as new memory
    await addMemory(query, "conversation");
    
    console.log("Context found:", context);
  };

  return (
    <button onClick={() => handleSubmit("Hello")} disabled={loading}>
      {loading ? "Loading..." : "Send"}
    </button>
  );
}

Environment Variables

bash
# .env.local
RETAINDB_API_KEY=rdb_your_key_here

Server Component Example

Use RetainDB directly in Server Components:

typescript
// app/dashboard/page.tsx
import { RetainDBClient } from "@retaindb/sdk";

const client = new RetainDBClient({
  apiKey: process.env.RETAINDB_API_KEY!,
});

export default async function Dashboard() {
  // Direct call - server-side only
  const profile = await client.memory.getUserProfile({
    user_id: "user-123",
  });

  return (
    <div>
      <h1>User Profile</h1>
      <pre>{JSON.stringify(profile, null, 2)}</pre>
    </div>
  );
}

Middleware for Auth

Protect your API routes:

typescript
// middleware.ts
import { NextResponse } from "next/server";
import type { NextRequest } from "next/server";

export function middleware(request: NextRequest) {
  // Verify user session
  const session = request.cookies.get("session");
  
  if (!session) {
    return NextResponse.redirect(new URL("/login", request.url));
  }

  // Add user ID to headers for API routes
  const response = NextResponse.next();
  response.headers.set("x-user-id", session.value);
  
  return response;
}

export const config = {
  matcher: ["/api/RetainDB/:path*"],
};

Caching with Next.js

Cache RetainDB responses:

typescript
// app/api/chat/route.ts
export const dynamic = "force-dynamic";

export async function POST(request: Request) {
  // Your RetainDB logic
  const result = await client.memory.search({ ... });
  
  // Response is not cached by default
  return NextResponse.json(result);
}

Or use unstable_cache:

typescript
import { unstable_cache } from "next/cache";

const getCachedContext = unstable_cache(
  async (userId: string) => {
    return await client.memory.search({
      user_id: userId,
      query: "preferences",
    });
  },
  ["user-context"],
  { revalidate: 300 } // 5 minutes
);

Error Handling

typescript
// app/api/chat/route.ts
export async function POST(request: Request) {
  try {
    const result = await client.memory.search({ ... });
    return NextResponse.json(result);
  } catch (error) {
    console.error(RetainDB error:),
  @(## What RetainDB actually does, ## What RetainDB actually does),
  @(Add RetainDB to your Claude Code, Add RetainDB to your Claude Code),
  @(Add a RetainDB server entry, Add a RetainDB server entry),
  @(call a simple RetainDB tool, call a simple RetainDB tool),
  @(indexed RetainDB retrieval, indexed RetainDB retrieval),
  @(want RetainDB to rank, want RetainDB to rank),
  @(do not want RetainDB to scan, do not want RetainDB to scan),
  @(For indexed RetainDB context, For indexed RetainDB context),
  @(shipping RetainDB docs, shipping RetainDB docs),
  @(discovering RetainDB, not, discovering RetainDB, not),
  @(description: "Contributor-facing rules for shipping RetainDB docs, description: Contributor-facing rules for shipping RetainDB docs", error);
    
    return NextResponse.json(
      { error: "Failed to get context" },
      { status: 500 }
    );
  }
}

Next step

Was this page helpful?

Your feedback helps us prioritize docs improvements weekly.