Persistence
Save and load chat history with the Prisma adapter from @supyagent/sdk/prisma.
Persistence
The @supyagent/sdk/prisma sub-package provides a ChatAdapter for persisting chat messages using Prisma. It works with SQLite (local dev) and PostgreSQL (production).
Setup
1. Install Prisma
npm install @prisma/client prisma2. Add the Schema
Add Chat and Message models to your Prisma schema:
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "sqlite" // or "postgresql"
url = env("DATABASE_URL")
}
model Chat {
id String @id @default(cuid())
title String @default("New Chat")
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
messages Message[]
}
model Message {
id String @id @default(cuid())
chatId String
chat Chat @relation(fields: [chatId], references: [id], onDelete: Cascade)
role String
parts String
metadata String?
createdAt DateTime @default(now())
@@index([chatId])
}3. Generate and Push
npx prisma generate
npx prisma db pushUsage
import { PrismaClient } from '@prisma/client';
export const prisma = new PrismaClient();import { createPrismaAdapter } from '@supyagent/sdk/prisma';
import { prisma } from '@/lib/prisma';
const adapter = createPrismaAdapter(prisma);ChatAdapter Interface
The adapter implements the ChatAdapter interface:
adapter.saveChat(chatId, messages)
Save or update a chat with its current messages. Uses upsert — safe to call repeatedly with the full message list.
await adapter.saveChat(chatId, messages);The title is automatically extracted from the first user message (truncated to 100 characters).
adapter.loadChat(chatId)
Load all messages for a chat, ordered by creation time.
const messages = await adapter.loadChat(chatId);
// => UIMessageLike[]adapter.listChats()
List all chats (most recently updated first).
const chats = await adapter.listChats();
// => Array<{ id, title, createdAt, updatedAt }>adapter.deleteChat(chatId)
Delete a chat and all its messages (cascade).
await adapter.deleteChat(chatId);Using with streamText
The typical pattern is to save messages on both step completion and final completion:
const result = streamText({
model: yourModel,
messages,
tools,
});
return result.toUIMessageStreamResponse({
originalMessages: messages,
onStepFinish: async ({ messages: updated }) => {
await adapter.saveChat(chatId, updated);
},
onFinish: async ({ messages: updated }) => {
await adapter.saveChat(chatId, updated);
},
});Building API Routes for Chat Management
import { createPrismaAdapter } from '@supyagent/sdk/prisma';
import { prisma } from '@/lib/prisma';
const adapter = createPrismaAdapter(prisma);
// List all chats
export async function GET() {
const chats = await adapter.listChats();
return Response.json(chats);
}// Load a specific chat
export async function GET(req: Request, { params }: { params: { id: string } }) {
const messages = await adapter.loadChat(params.id);
return Response.json(messages);
}
// Delete a chat
export async function DELETE(req: Request, { params }: { params: { id: string } }) {
await adapter.deleteChat(params.id);
return new Response(null, { status: 204 });
}PostgreSQL
Switch from SQLite to PostgreSQL by changing the datasource in your Prisma schema:
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}The adapter works identically with both databases — no code changes needed.
What's Next
- Context Management — Automatic summarization when conversations get long
- Full API Route Example — See persistence in a complete endpoint