Frameworks
Next.js
App Router route handlers, server actions, and streaming chat endpoints.
What this guide does not cover
- Covers App Router route handlers and server actions. Pages Router API routes work the same way but are not shown.
- Guarding inside a Client Component is not supported: the key would be exposed to the browser.
Step 1: Install
npm install @soterai/coreStep 2: Environment variables
Add these to your .env.local file:
# 📁 .env.local - server-side only!
SOTER_API_KEY=ck_live_your_key_here
SOTER_PROJECT_ID= # optionalNEXT_PUBLIC_ are exposed to the browser. Always keep SoterAI credentials server-side only.Step 3: Route Handler (manual control)
Full control over the guard flow in your API route:
1// app/api/chat/route.ts
2import { Soter } from "@soterai/core";
3
4const soter = new Soter({
5 apiKey: process.env.SOTER_API_KEY,
6 projectId: process.env.SOTER_PROJECT_ID,
7});
8
9export async function POST(req: Request) {
10 const body = await req.json();
11
12 // 1. Guard user input before LLM
13 const result = await soter.protect({
14 input: body.message,
15 context: { sessionId: body.sessionId },
16 });
17
18 if (!result.allowed) {
19 // 🛑 Blocked before LLM is called
20 return Response.json(
21 { blocked: true, reason: result.reason },
22 { status: 403 },
23 );
24 }
25
26 // 2. Call LLM with safe text
27 const safeMessage = result.safeText ?? body.message;
28 const llmReply = await callYourLLM(safeMessage);
29
30 // 3. Guard model output before user sees it
31 const output = await soter.guardOutput({ text: llmReply });
32
33 return Response.json({
34 reply: output.safeText ?? output.redactedText ?? llmReply,
35 blocked: !output.allowed,
36 });
37}Step 4: One-line helper (recommended)
Use createGuardedRoute from @soterai/core/nextfor a minimal setup:
// app/api/chat/route.ts — one-line guarded route
import { createGuardedRoute } from "@soterai/core/next";
export const runtime = "nodejs";
export const POST = createGuardedRoute({
apiKey: process.env.SOTER_API_KEY!,
callLLM: async (safeInput) => myLLMCall(safeInput),
});
// The browser sends { message } and receives { reply, blocked }createGuardedRoute helper automatically handles input guard → LLM → output guard flow. The browser sends { message } and receives{ reply, blocked }. The API key never leaves your server.Step 5: Server Actions
Guard inputs directly in server actions without exposing the API key:
1"use server";
2import { Soter } from "@soterai/core";
3import { revalidatePath } from "next/cache";
4
5const soter = new Soter({
6 apiKey: process.env.SOTER_API_KEY,
7});
8
9export async function submitMessage(formData: FormData) {
10 const message = formData.get("message") as string;
11
12 // Guard the input inside the server action
13 const result = await soter.protect({
14 input: message,
15 context: { sessionId: crypto.randomUUID() },
16 });
17
18 if (!result.allowed) {
19 return { error: "Message blocked", reason: result.reason };
20 }
21
22 // Only call the LLM if the input is safe
23 const reply = await callLLM(result.safeText ?? message);
24 revalidatePath("/chat");
25 return { reply };
26}Step 6: Client component (safe pattern)
The client component only talks to your server, never to SoterAI directly:
1// app/chat/page.tsx
2"use client";
3import { useState } from "react";
4
5export default function ChatPage() {
6 const [reply, setReply] = useState("");
7
8 async function handleSubmit(e: React.FormEvent<HTMLFormElement>) {
9 e.preventDefault();
10 const form = new FormData(e.currentTarget);
11
12 // Send to YOUR server, not directly to SoterAI
13 const res = await fetch("/api/chat", {
14 method: "POST",
15 body: JSON.stringify({
16 message: form.get("message")
17 }),
18 headers: { "Content-Type": "application/json" },
19 });
20
21 const data = await res.json();
22 setReply(data.reply ?? data.reason);
23 }
24
25 return (
26 <form onSubmit={handleSubmit} className="space-y-4">
27 <input
28 name="message"
29 placeholder="Type your message..."
30 className="input"
31 />
32 <button type="submit" className="button-primary">
33 Send
34 </button>
35 {reply && <p className="text-slate-300">{reply}</p>}
36 </form>
37 );
38}Key points for Next.js developers
Don't use NEXT_PUBLIC_
SoterAI API keys must never be exposed to the browser.
Always guard both ways
Protect both input (before LLM) and output (after LLM).
Use Route Handlers
Route handlers run server-side and are perfect for SoterAI integration.
Server Actions work too
Guard messages directly in server actions for form-based chat.
App Router recommended
All examples use the App Router, but the basic pattern works with Pages Router too.
Example project
See examples/nextjs-chatbot for a complete working Next.js example.