# How to Build a Full-Stack SaaS App with Next.js?

## Why Next.js for SaaS

Next.js is the go-to framework for building modern SaaS applications in 2025. It gives you server-side rendering, API routes, and seamless deployment — all in one package. Combined with MongoDB and Stripe, you can ship a complete SaaS product faster than ever.

In this guide, I'll show you exactly how I build full-stack SaaS apps — from project setup to live deployment.

## What You'll Need

Before we start, make sure you have these ready:

*   ✅ Node.js — v18 or higher
    
*   ✅ MongoDB Atlas account
    
*   ✅ Stripe account — for payments
    
*   ✅ Vercel account — for deployment
    
*   ✅ Basic knowledge of React and JavaScript
    

## 🧰 Tech Stack

| Tool | Role |
| --- | --- |
| **Next.js 14** | Full-stack framework |
| **MongoDB Atlas** | Cloud database |
| **Mongoose** | MongoDB ORM |
| **NextAuth.js** | Authentication |
| **Stripe** | Subscription payments |
| **Tailwind CSS** | Styling |
| **Vercel** | Deployment |

## Step 1 — Project Setup

Open your terminal and run:

```bash
npx create-next-app@latest my-saas-app
cd my-saas-app
npm install mongoose next-auth stripe @stripe/stripe-js
```

Create your .env.local file:

```javascript
MONGODB_URI=your_mongodb_connection_string
GOOGLE_ID=your_google_client_id
GOOGLE_SECRET=your_google_client_secret
NEXTAUTH_SECRET=your_random_secret
NEXTAUTH_URL=http://localhost:3000
STRIPE_SECRET_KEY=your_stripe_secret_key
STRIPE_PRICE_ID=your_stripe_price_id
NEXT_PUBLIC_URL=http://localhost:3000
```

## Step 2 — Connect MongoDB

Create /lib/mongodb.js:

```bash
import mongoose from 'mongoose'

const connectDB = async () => {
  if (mongoose.connections[0].readyState) return
  
  try {
    await mongoose.connect(process.env.MONGODB_URI)
    console.log('MongoDB connected successfully')
  } catch (error) {
    console.error('MongoDB connection error:', error)
  }
}

export default connectDB
```

## Step 3 — Setup Authentication

Create /app/api/auth/\[...nextauth\]/route.js:

```javascript
import NextAuth from 'next-auth'
import GoogleProvider from 'next-auth/providers/google'
import connectDB from '@/lib/mongodb'
import User from '@/models/User'

export const authOptions = {
  providers: [
    GoogleProvider({
      clientId: process.env.GOOGLE_ID,
      clientSecret: process.env.GOOGLE_SECRET,
    })
  ],
  callbacks: {
    async signIn({ user }) {
      await connectDB()
      const existingUser = await User.findOne({ email: user.email })
      if (!existingUser) {
        await User.create({
          name: user.name,
          email: user.email,
          image: user.image,
        })
      }
      return true
    }
  }
}

const handler = NextAuth(authOptions)
export { handler as GET, handler as POST }
```

## Step 4 — Stripe Subscription

Create /app/api/create-checkout/route.js:

```javascript
import Stripe from 'stripe'
import { getServerSession } from 'next-auth'
import { authOptions } from '../auth/[...nextauth]/route'

const stripe = new Stripe(process.env.STRIPE_SECRET_KEY)

export async function POST(req) {
  const session = await getServerSession(authOptions)
  
  if (!session) {
    return Response.json({ error: 'Unauthorized' }, { status: 401 })
  }

  const checkoutSession = await stripe.checkout.sessions.create({
    mode: 'subscription',
    customer_email: session.user.email,
    payment_method_types: ['card'],
    line_items: [{
      price: process.env.STRIPE_PRICE_ID,
      quantity: 1,
    }],
    success_url: `${process.env.NEXT_PUBLIC_URL}/dashboard?success=true`,
    cancel_url: `${process.env.NEXT_PUBLIC_URL}/pricing`,
  })

  return Response.json({ url: checkoutSession.url })
}
```

## Step 5 — Protect Routes

Create middleware.js in root:

```javascript
export { default } from 'next-auth/middleware'

export const config = {
  matcher: ['/dashboard/:path*', '/settings/:path*']
}
```

## Step 6 — Handle Stripe Webhooks

Create /app/api/webhooks/stripe/route.js:

```bash
import Stripe from 'stripe'
import connectDB from '@/lib/mongodb'
import User from '@/models/User'

const stripe = new Stripe(process.env.STRIPE_SECRET_KEY)

export async function POST(req) {
  const body = await req.text()
  const sig = req.headers.get('stripe-signature')

  let event
  try {
    event = stripe.webhooks.constructEvent(
      body, sig, process.env.STRIPE_WEBHOOK_SECRET
    )
  } catch (err) {
    return Response.json({ error: 'Webhook error' }, { status: 400 })
  }

  await connectDB()

  if (event.type === 'checkout.session.completed') {
    const session = event.data.object
    await User.findOneAndUpdate(
      { email: session.customer_email },
      { isPro: true }
    )
  }

  return Response.json({ received: true })
}
```

## Step 7 — Deploy on Vercel

```plaintext
# Install Vercel CLI
npm install -g vercel

# Deploy
vercel

# Set environment variables in Vercel dashboard
# Then deploy to production
vercel --prod
```

## Wrapping Up

Building a SaaS app with Next.js becomes easy when you break it into small steps:

* ✅ Setup project
* ✅ Connect database
* ✅ Add authentication
* ✅ Integrate payments
* ✅ Protect routes
* ✅ Handle webhooks
* ✅ Deploy

> The best SaaS is the one that ships. Start small, iterate fast, and keep building.

*Found this helpful? Follow me for more full-stack development content. 🚀*
