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.

2 min read

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:

  1. currentUser() or 401.
  2. Reject a missing prompt, or one longer than 4000 characters, with 400.
  3. Sum Generation.credits since the first of the month. If that sum is at least the allowance, return 402.
  4. Open the upstream stream.
  5. When the stream finishes, insert the Generation row.

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.

Product updates for developers shipping with Designyff kits.

Purchased source may be used in personal, commercial, and client projects. Do not redistribute or resell the original source. Terms of Service

Designyff © 2026 | Starter kits for developers | Powered by  Tacko