AI
Building an AI SaaS with Next.js
Structure a Next.js AI product around a provider adapter, a generation log, monthly credits, and Stripe plan state.
An AI SaaS is a normal SaaS with one expensive route. The AI SaaS Starter keeps that route behind a signed-in user, a credit check, and a small adapter. The stack is Next.js 15, React 19, Prisma 6 on SQLite, and Stripe Node SDK 17. Streaming is covered in streaming AI responses in Next.js. Credits are covered in AI usage limits and credits.
Tables
model User {
id String @id @default(cuid())
email String @unique
plan String @default("free")
stripeCustomerId String? @unique
stripeSubscriptionId String?
generations Generation[]
}
model Generation {
id String @id @default(cuid())
userId String
user User @relation(fields: [userId], references: [id])
prompt String
output String
credits Int
createdAt DateTime @default(now())
}
User.plan is the same idea as the Stripe Subscription Starter: the webhook sets it, the app reads it. Generation is the history list and the usage ledger. Each completed response inserts one row with credits: 1.
The adapter boundary
src/lib/ai/provider.ts is the only file that knows the HTTP shape of the model API. It POSTs to ${AI_BASE_URL}/chat/completions, defaulting AI_BASE_URL to https://api.openai.com/v1 and AI_MODEL to gpt-4o-mini. The route handler does not import an OpenAI SDK. To point at another OpenAI-compatible endpoint, change AI_BASE_URL and AI_API_KEY. Replacing the adapter is a file change, not a rewrite of the chat page.
Request order
POST /api/generate does the checks in this order:
currentUser()or401.- Reject a missing prompt, or one longer than 4000 characters, with
400. - Sum
Generation.creditssince the first of the month. If that sum is at least the allowance, return402. - Open the upstream stream.
- When the stream finishes, insert the
Generationrow.
The credit row is written after the stream completes, not before. A failed upstream request throws before the insert. A client that disconnects mid-stream can still hit the pull completion path; the kit records whatever text it accumulated.
Billing
Free and Pro are the two allowances. Stripe Checkout and the webhook live next to the generator, not inside it. Do not check the Stripe API from /api/generate. Read user.plan.
Want to skip the setup? The AI SaaS Starter already includes the chat route, the provider adapter, the credit ledger, and Stripe plan state.