Next.js RAG with sources: cited answers in an App Router app

A chat box is easy in Next.js. A chat box whose answers a compliance officer will accept is not. The difference is provenance, and that is the part this SDK carries for you.

Last updated · SDK v1.1.0 · changelog

Short answer: install @uthereal-sdk/cortex, create app/api/cortex/route.ts with createCortexHandler, and call it from a client component with createCortexBrowserClient. Render answers with CitedAnswer so every claim shows its source. CORTEX_SHARED_API_KEY stays in server environment variables.

01

The route handler

app/api/cortex/route.ts
// app/api/cortex/route.ts
import { createCortexHandler } from "@uthereal-sdk/cortex/server";
import { auth } from "@/lib/auth";

const handler = createCortexHandler({
  config: {
    baseUrl: process.env.CORTEX_API_BASE_URL!,
    assistantId: process.env.CORTEX_ASSISTANT_ID!,
    apiKey: process.env.CORTEX_SHARED_API_KEY!, // server only
  },
  allowedOrigin: process.env.NEXT_PUBLIC_SITE_URL!,
  authenticate: async (request) => {
    const session = await auth(request);
    return session ? { id: session.user.id, externalUserId: session.user.id } : null;
  },
  store: yourStoreAdapter,
});

export const POST = handler;
export const GET = handler;

Never import the server entry point from a client component. The key belongs to the route handler; the browser only ever talks to your own endpoint.

02

The client component

components/ask.tsx
"use client";
import { createCortexBrowserClient } from "@uthereal-sdk/cortex/browser";
import { CitedAnswer } from "@uthereal-sdk/cortex/react";

const cortex = createCortexBrowserClient({ endpoint: "/api/cortex" });

export function Ask({ message }: { message: string }) {
  // stream the answer, then render the saved message with citations
  return <CitedAnswer answer={answer} messageId={id} loadPdf={cortex.pdf} />;
}

03

Streaming and refusals

  • Answers stream as NDJSON snapshots, so the first words appear immediately
  • Each snapshot carries the claims and references resolved so far
  • When no passage supports the question, the agent refuses instead of inventing
  • Retrieval-only mode returns ranked JSON if you want to drive your own model

04

Production checklist

  • API key in server environment variables, never NEXT_PUBLIC_
  • Authenticate every request in the route handler before forwarding
  • Scope conversation identifiers to the signed-in user, not to client input
  • Set allowedOrigin to your deployed domain
  • Clear client caches on sign-out so evidence is never shared between accounts

Frequently asked questions

How do I add RAG to a Next.js app?
Install @uthereal-sdk/cortex, expose one route handler at app/api/cortex/route.ts created with createCortexHandler, and call it from a client component with createCortexBrowserClient. The Cortex API key stays in server environment variables.
Does it work with the App Router and Server Components?
Yes. The handler is a standard Web Request and Response function, so it runs in a Route Handler on the Node.js or Edge runtime. The streaming client is a client component; Server Components can render everything around it.
Can I deploy it to Vercel Edge or Cloudflare?
Yes. The package is ESM with no Node-only native bindings, so the server entry runs on Node.js 22+, Vercel Edge, Cloudflare Workers, Deno 2 and Bun.
How do citations render in React?
The CitedAnswer component from @uthereal-sdk/cortex/react renders claim-level citations; PdfEvidence opens the cited page with the supporting passage highlighted.
Do I need a vector database in Next.js?
No. Retrieval, ranking and citation mapping happen in the hosted Cortex service, so there is no vector store, embedding job or reranker to run alongside your Next.js deployment.

Related

Ship a knowledge agent your users can trust

Create an agent, point the SDK at it, and let your AI coding tool do the wiring.

Questions? sdk@uthereal.ai