In this comprehensive tutorial, you'll learn how to build a production-ready full-stack application using the latest technologies and best practices. By the end, you'll have a complete understanding of how to build, test, and deploy modern web applications.
Prerequisites
Before we begin, make sure you have the following ready:
- Node.js 18+: Download from nodejs.org
- Basic knowledge of React and TypeScript: You should be comfortable with components, hooks, and type definitions
- PostgreSQL database: Either local installation or a cloud provider like Supabase or Neon
- Text editor: VS Code recommended with ESLint and Prettier extensions
- Git: For version control and deployment
Step 1: Project Setup
Let's start by creating a new Next.js project with all the modern tools configured. We'll use the official Next.js CLI which sets up everything we need.
Initialize the Project
Open your terminal and run:
npx create-next-app@latest my-fullstack-app --typescript --tailwind --app
cd my-fullstack-app
npm install
This command creates a new Next.js 15 project with TypeScript, Tailwind CSS, and the App Router already configured.
Project Structure
Your initial project structure should look like this:
my-fullstack-app/
├── app/
│ ├── layout.tsx
│ └── page.tsx
├── public/
├── package.json
├── tsconfig.json
└── tailwind.config.js
Step 2: Database Configuration
Set up Prisma as your ORM and connect it to PostgreSQL. Prisma provides type-safe database access and makes migrations easy.
Install Prisma
npm install prisma @prisma/client
npx prisma init
Configure Your Database
Update your .env file with your database connection string:
DATABASE_URL="postgresql://username:password@localhost:5432/mydb"
Create Your Schema
Edit prisma/schema.prisma to define your data model:
model User {
id String @id @default(cuid())
email String @unique
name String?
posts Post[]
createdAt DateTime @default(now())
}
model Post {
id String @id @default(cuid())
title String
content String?
published Boolean @default(false)
author User @relation(fields: [authorId], references: [id])
authorId String
createdAt DateTime @default(now())
}
Run Migrations
npx prisma migrate dev --name init
npx prisma generate
Step 3: Authentication
Implement authentication using NextAuth.js with email/password and OAuth providers like Google and GitHub.
Install NextAuth
npm install next-auth @auth/prisma-adapter
npm install bcryptjs
npm install -D @types/bcryptjs
Configure NextAuth
Create app/api/auth/[...nextauth]/route.ts:
import NextAuth from "next-auth"
import GithubProvider from "next-auth/providers/github"
import GoogleProvider from "next-auth/providers/google"
export const authOptions = {
providers: [
GithubProvider({
clientId: process.env.GITHUB_ID,
clientSecret: process.env.GITHUB_SECRET,
}),
GoogleProvider({
clientId: process.env.GOOGLE_ID,
clientSecret: process.env.GOOGLE_SECRET,
}),
],
}
const handler = NextAuth(authOptions)
export { handler as GET, handler as POST }
Step 4: API Routes
Create API routes for CRUD operations. We'll use Server Actions for mutations and API routes for queries, following Next.js 15 best practices.
Create a Server Action
Create app/actions/posts.ts:
'use server'
import { prisma } from '@/lib/prisma'
import { revalidatePath } from 'next/cache'
export async function createPost(formData: FormData) {
const title = formData.get('title') as string
const content = formData.get('content') as string
await prisma.post.create({
data: { title, content, authorId: 'user-id' }
})
revalidatePath('/posts')
}
Step 5: Frontend Components
Build reusable React components with proper TypeScript types and responsive design. We'll create components for displaying and creating posts.
Post List Component
import { prisma } from '@/lib/prisma'
export default async function PostList() {
const posts = await prisma.post.findMany({
include: { author: true },
orderBy: { createdAt: 'desc' }
})
return (
{posts.map(post => (
{post.title}
{post.content}
By {post.author.name}
))}
)
}
Step 6: Deployment
Deploy your application to Vercel with automatic CI/CD, environment variables, and database migrations.
Push to GitHub
git init
git add .
git commit -m "Initial commit"
git remote add origin your-repo-url
git push -u origin main
Deploy to Vercel
- Go to vercel.com and sign in
- Click "New Project"
- Import your GitHub repository
- Add environment variables (DATABASE_URL, NEXTAUTH_SECRET, etc.)
- Click "Deploy"
Testing
Add tests to ensure your application works correctly:
npm install -D @testing-library/react @testing-library/jest-dom jest
npm install -D @types/jest
Next Steps
You now have a fully functional full-stack application! Here are some ideas to expand your project:
- Real-time updates: Add WebSocket support for live updates
- File uploads: Integrate cloud storage like AWS S3 or Cloudinary
- Advanced search: Implement full-text search with Postgres or Algolia
- Email notifications: Send emails using SendGrid or Resend
- Payment processing: Integrate Stripe for subscriptions
Conclusion
Congratulations! You've built a production-ready full-stack application with modern best practices. Continue learning by adding features, optimizing performance, and exploring advanced patterns like caching, background jobs, and microservices.