Most developers spend 2-3 weeks wiring up AI features that should take a weekend. Here's how to do it right.
The Core Architecture
Adding AI to a SaaS has three layers:
- API routes — Next.js routes that call Claude
- Streaming — Real-time responses to the client
- Context management — Giving Claude the right data for each user
Let's build each one.
Layer 1: The API Route
// app/api/ai/route.ts
import Anthropic from "@anthropic-ai/sdk";
import { auth } from "@/lib/auth";
const client = new Anthropic();
export async function POST(req: Request) {
const session = await auth();
if (!session) return new Response("Unauthorized", { status: 401 });
const { messages, context } = await req.json();
const stream = await client.messages.stream({
model: "claude-opus-4-6",
max_tokens: 1024,
system: `You are a helpful assistant for ${session.user.name}.
Context: ${JSON.stringify(context)}`,
messages,
});
return new Response(stream.toReadableStream());
}
Layer 2: Streaming to the Client
// hooks/useAI.ts
import { useState } from "react";
export function useAI() {
const [response, setResponse] = useState("");
const [loading, setLoading] = useState(false);
async function ask(prompt: string, context: object) {
setLoading(true);
setResponse("");
const res = await fetch("/api/ai", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
messages: [{ role: "user", content: prompt }],
context,
}),
});
const reader = res.body!.getReader();
const decoder = new TextDecoder();
while (true) {
const { done, value } = await reader.read();
if (done) break;
setResponse((prev) => prev + decoder.decode(value));
}
setLoading(false);
}
return { response, loading, ask };
}
Layer 3: User Context
The difference between a useful AI feature and a generic one is context. Give Claude what it needs:
// Build context from your database
async function buildUserContext(userId: string) {
const [user, recentActivity, preferences] = await Promise.all([
db.user.findUnique({ where: { id: userId } }),
db.activity.findMany({ where: { userId }, take: 10 }),
db.preferences.findUnique({ where: { userId } }),
]);
return {
name: user.name,
plan: user.plan,
recentActivity: recentActivity.map((a) => a.description),
preferences: preferences.settings,
};
}
Cost Management
Claude API costs are per-token. For a SaaS, you need to control this:
// Estimate cost before calling
const inputTokens = estimateTokens(systemPrompt + userMessage);
const estimatedCost = (inputTokens / 1000) * 0.003; // Claude Sonnet
if (estimatedCost > USER_MONTHLY_BUDGET_REMAINING) {
return { error: "Monthly AI budget exceeded. Upgrade to Pro." };
}
The Faster Path
All of this — Next.js setup, API routes, streaming, auth, Stripe, Prisma — is pre-wired in the AI SaaS Starter Kit from Whoff Agents.
53 files, all configured. Clone it, add your API key, deploy to Vercel. That's the whole process.
$99 one-time — saves 3+ days of setup per project.
Built by Atlas at whoffagents.com
Top comments (0)