<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Samuel Baiyi</title>
    <description>The latest articles on DEV Community by Samuel Baiyi (@samuel_baiyi_3b76314a314e).</description>
    <link>https://dev.to/samuel_baiyi_3b76314a314e</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3511718%2F4eedf636-ccaf-4789-819d-60d8a0fdcc9b.png</url>
      <title>DEV Community: Samuel Baiyi</title>
      <link>https://dev.to/samuel_baiyi_3b76314a314e</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/samuel_baiyi_3b76314a314e"/>
    <language>en</language>
    <item>
      <title>LaunchKit AI — Next.js SaaS Boilerplate for AI Apps</title>
      <dc:creator>Samuel Baiyi</dc:creator>
      <pubDate>Sat, 25 Apr 2026 01:30:37 +0000</pubDate>
      <link>https://dev.to/samuel_baiyi_3b76314a314e/launchkit-ai-nextjs-saas-boilerplate-for-ai-apps-2m12</link>
      <guid>https://dev.to/samuel_baiyi_3b76314a314e/launchkit-ai-nextjs-saas-boilerplate-for-ai-apps-2m12</guid>
      <description>&lt;p&gt;How I Structured a Production-Ready Next.js AI SaaS Boilerplate (And What I Learned)&lt;br&gt;
Every new project started the same way. Three weeks of setup before a single line of real product code. I finally got tired of it.&lt;br&gt;
The Problem Every Developer Knows&lt;br&gt;
You have an idea. A real one. An AI-powered SaaS that could genuinely help people.&lt;br&gt;
So you open your terminal and start a new Next.js project.&lt;br&gt;
And then it hits you.&lt;br&gt;
You need auth. Then billing. Then a database with proper Row Level Security. Then AI integration with streaming and token limits. Then transactional emails. Then a deploy pipeline.&lt;br&gt;
Three weeks later you're still configuring — and your motivation is half of what it was on day one.&lt;br&gt;
I've been a senior freelance frontend developer for years. I've built dozens of projects. And I kept repeating the same setup, project after project, client after client.&lt;br&gt;
So I stopped. And I built LaunchKit AI.&lt;br&gt;
What Is LaunchKit AI?&lt;br&gt;
LaunchKit AI is a production-ready Next.js 14 boilerplate built specifically for AI-powered SaaS products.&lt;br&gt;
It's opinionated by design. Every tool in the stack was chosen deliberately. You clone it, fill in your .env file, and you're shipping — not configuring.&lt;br&gt;
Here's the full stack:&lt;br&gt;
Next.js 14 (App Router + React Server Components)&lt;br&gt;
TypeScript — fully typed end to end&lt;br&gt;
Tailwind CSS + shadcn/ui&lt;br&gt;
Supabase — auth, database, storage, RLS&lt;br&gt;
Stripe — subscriptions, one-time payments, webhooks&lt;br&gt;
OpenAI SDK — streaming responses, token counting, per-plan limits&lt;br&gt;
Resend + React Email — transactional email templates&lt;br&gt;
Vercel — one-click deployment with CI/CD&lt;br&gt;
How I Structured It&lt;br&gt;
Here's the folder structure and the thinking behind each decision.&lt;br&gt;
launchkit-ai/&lt;br&gt;
├── app/&lt;br&gt;
│   ├── (auth)/&lt;br&gt;
│   │   ├── login/&lt;br&gt;
│   │   └── signup/&lt;a href="https://dev.tourl"&gt;&lt;/a&gt;&lt;br&gt;
│   ├── (dashboard)/&lt;br&gt;
│   │   ├── layout.tsx       ← protected layout with auth check&lt;br&gt;
│   │   ├── dashboard/&lt;br&gt;
│   │   └── settings/&lt;br&gt;
│   ├── api/&lt;br&gt;
│   │   ├── ai/&lt;br&gt;
│   │   │   └── stream/      ← OpenAI streaming endpoint&lt;br&gt;
│   │   ├── stripe/&lt;br&gt;
│   │   │   └── webhook/     ← Stripe webhook handler&lt;br&gt;
│   │   └── auth/&lt;br&gt;
│   └── layout.tsx&lt;br&gt;
├── components/&lt;br&gt;
│   ├── ui/                  ← shadcn/ui base components&lt;br&gt;
│   ├── ai/                  ← AI-specific components&lt;br&gt;
│   └── billing/             ← Stripe portal + plan display&lt;br&gt;
├── lib/&lt;br&gt;
│   ├── supabase/&lt;br&gt;
│   │   ├── client.ts        ← browser client&lt;br&gt;
│   │   └── server.ts        ← server client with cookies&lt;br&gt;
│   ├── stripe.ts&lt;br&gt;
│   ├── openai.ts&lt;br&gt;
│   └── resend.ts&lt;br&gt;
├── hooks/&lt;br&gt;
│   ├── useAI.ts             ← reusable AI streaming hook&lt;br&gt;
│   ├── useSubscription.ts&lt;br&gt;
│   └── useUser.ts&lt;br&gt;
├── types/&lt;br&gt;
│   └── index.ts             ← global TypeScript types&lt;br&gt;
├── prisma/&lt;br&gt;
│   └── schema.prisma        ← User, Subscription, UsageLog models&lt;br&gt;
├── emails/&lt;br&gt;
│   ├── WelcomeEmail.tsx&lt;br&gt;
│   ├── ResetPasswordEmail.tsx&lt;br&gt;
│   └── InvoiceEmail.tsx&lt;br&gt;
└── .env.example&lt;br&gt;
The 3 Hardest Parts to Get Right&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Supabase Auth + Middleware
Getting Supabase auth to work cleanly with Next.js App Router took the most iteration. The key is using two separate Supabase clients — one for the browser, one for the server — and handling session refresh in middleware.
// middleware.ts
import { createMiddlewareClient } from '@supabase/auth-helpers-nextjs'
import { NextResponse } from 'next/server'&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;export async function middleware(req) {&lt;br&gt;
  const res = NextResponse.next()&lt;br&gt;
  const supabase = createMiddlewareClient({ req, res })&lt;/p&gt;

&lt;p&gt;// Refresh session on every request&lt;br&gt;
  const { data: { session } } = await supabase.auth.getSession()&lt;/p&gt;

&lt;p&gt;// Protect dashboard routes&lt;br&gt;
  if (!session &amp;amp;&amp;amp; req.nextUrl.pathname.startsWith('/dashboard')) {&lt;br&gt;
    return NextResponse.redirect(new URL('/login', req.url))&lt;br&gt;
  }&lt;/p&gt;

&lt;p&gt;return res&lt;br&gt;
}&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;OpenAI Streaming with Per-Plan Limits
The AI streaming endpoint needed to do three things at once: stream the response to the client, count tokens in real time, and enforce usage limits based on the user's Stripe plan.
// app/api/ai/stream/route.ts
export async function POST(req: Request) {
const { messages, userId } = await req.json()&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;// Check plan limits before calling OpenAI&lt;br&gt;
  const usage = await getUserUsage(userId)&lt;br&gt;
  const limit = await getPlanLimit(userId)&lt;/p&gt;

&lt;p&gt;if (usage &amp;gt;= limit) {&lt;br&gt;
    return new Response('Usage limit reached', { status: 429 })&lt;br&gt;
  }&lt;/p&gt;

&lt;p&gt;const stream = await openai.chat.completions.create({&lt;br&gt;
    model: 'gpt-4o',&lt;br&gt;
    messages,&lt;br&gt;
    stream: true,&lt;br&gt;
  })&lt;/p&gt;

&lt;p&gt;// Stream + count tokens simultaneously&lt;br&gt;
  return new StreamingTextResponse(stream)&lt;br&gt;
}&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Stripe Webhook Reliability
Webhooks fail silently if you're not careful. The boilerplate includes a dedicated webhook handler that verifies the Stripe signature, handles all critical events, and updates Supabase in a single transaction.
// app/api/stripe/webhook/route.ts
const relevantEvents = new Set([
'checkout.session.completed',
'customer.subscription.updated',
'customer.subscription.deleted',
'invoice.payment_succeeded',
'invoice.payment_failed',
])&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;export async function POST(req: Request) {&lt;br&gt;
  const body = await req.text()&lt;br&gt;
  const sig = req.headers.get('stripe-signature')!&lt;/p&gt;

&lt;p&gt;let event: Stripe.Event&lt;/p&gt;

&lt;p&gt;try {&lt;br&gt;
    event = stripe.webhooks.constructEvent(&lt;br&gt;
      body, sig, process.env.STRIPE_WEBHOOK_SECRET!&lt;br&gt;
    )&lt;br&gt;
  } catch (err) {&lt;br&gt;
    return new Response('Webhook signature failed', { status: 400 })&lt;br&gt;
  }&lt;/p&gt;

&lt;p&gt;if (relevantEvents.has(event.type)) {&lt;br&gt;
    await handleStripeEvent(event)&lt;br&gt;
  }&lt;/p&gt;

&lt;p&gt;return new Response(null, { status: 200 })&lt;br&gt;
}&lt;br&gt;
What I Learned Building This&lt;br&gt;
Ship opinionated products. The most tempting thing was to make everything configurable. A flag for every choice. Support for every database. That's how you build a product nobody can understand. I picked the best tools and committed to them.&lt;br&gt;
Documentation is the product. Half of the value is the README and setup guide. A brilliant codebase with no explanation is useless to someone trying to ship fast.&lt;br&gt;
Your experience is worth packaging. I've spent years learning what breaks, what scales, and what's a waste of time. That knowledge lives in this boilerplate — and developers will pay for it.&lt;br&gt;
What's Next&lt;br&gt;
LaunchKit AI is now live on Gumroad. If you're tired of rebuilding the same foundation for every project, it might be exactly what you need.&lt;/p&gt;

&lt;p&gt;👉 Get LaunchKit AI on Gumroad&lt;br&gt;
&lt;a href="https://sirsamdev.gumroad.com/l/eqgix" rel="noopener noreferrer"&gt;https://sirsamdev.gumroad.com/l/eqgix&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Starter — $197 (core stack)&lt;br&gt;
Pro — $297 (full kit + lifetime updates + Discord community)&lt;br&gt;
Pay once. Build unlimited projects. 7-day refund guarantee.&lt;br&gt;
Built by a senior freelance dev, for devs who want to stop configuring and start shipping.&lt;br&gt;
Drop a comment if you have questions about the architecture — happy to go deeper on any part of the stack.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>webdev</category>
      <category>openai</category>
      <category>hackathon</category>
    </item>
  </channel>
</rss>
