Developer's Blog Platform β Next.js, MongoDB, Prisma, TanStack React Query FullStack Project (Blog for Coding Errors & Solutions)
A full-stack developer blog platform where programmers share real-world coding errors, bugs, and their solutions. Built with Next.js 16, React 19, TypeScript, MongoDB, Prisma, and TanStack React Query.
- Live-Demo: https://dev-bug-coder-blog.vercel.app/
- Overview
- Features
- Technology Stack
- Project Structure
- Getting Started
- Environment Variables
- Installation & Setup
- Running the Project
- API Endpoints
- Components Documentation
- Custom Hooks
- Database Schema
- Reusing Components
- Deployment
- Keywords
- Conclusion
Dev-Bug-Coder-Blog is a modern, full-stack blogging platform designed for developers to share coding errors, bugs, and solutions. It features real-time interactions, optimistic UI updates, nested comments, image uploads, and a comprehensive notification system.
- Server-Side Rendering (SSR) with Next.js App Router
- Client-Side Rendering (CSR) with React Query for instant updates
- Optimistic Updates for seamless user experience
- Real-time Notifications system
- Nested Comments with threading support
- Image Upload with ImageKit integration
- Authentication with JWT tokens
- Responsive Design with Tailwind CSS
-
User Authentication
- Registration with avatar upload
- Login/Logout
- Password reset via email
- JWT-based session management
-
Post Management
- Create, edit, and delete posts
- Rich text content with code snippets
- Image/screenshot uploads
- Tag system for categorization
- Like and helpful marks
- Save/unsave posts
-
Comments System
- Nested comment threads
- Reply to comments
- Like comments
- Edit and delete own comments
- Image uploads in comments
-
Notifications
- Real-time notifications for interactions
- Mark as read/unread
- Notification count badge
-
Search & Filter
- Search posts by title, content, tags
- Filter by tags
- Recent posts sidebar
- Popular topics
-
Admin Features
- View and manage reported posts
- Admin dashboard
- Next.js 16.1.1 - React framework with App Router
- React 19.0.0 - UI library
- TypeScript 5.8.3 - Type safety
- Tailwind CSS 3.4.17 - Utility-first CSS
- TanStack React Query 5.62.8 - Data fetching and caching
- ShadCN UI - Reusable component library
- Radix UI - Accessible component primitives
- Next.js API Routes - Serverless API endpoints
- Prisma 6.19.1 - Type-safe ORM
- MongoDB - NoSQL database
- JWT - Authentication tokens
- bcrypt - Password hashing
- ImageKit - Image hosting and optimization
- Nodemailer - Email sending (password reset)
- React Icons - Icon library
- Lucide React - Additional icons
dev-blog/
βββ app/ # Next.js App Router
β βββ api/ # API routes
β β βββ auth/ # Authentication endpoints
β β βββ posts/ # Post CRUD operations
β β βββ comments/ # Comment operations
β β βββ notifications/ # Notification endpoints
β β βββ upload/ # Image upload
β β βββ users/ # User endpoints
β βββ (pages)/ # Public pages
β β βββ page.tsx # Home page
β β βββ posts/ # Posts listing
β β βββ post/[id]/ # Post details
β β βββ login/ # Login page
β β βββ register/ # Registration page
β β βββ create-post/ # Create post
β β βββ edit-post/[id]/ # Edit post
β β βββ saved-posts/ # Saved posts
β β βββ notifications/ # Notifications page
β β βββ admin/ # Admin pages
β βββ layout.tsx # Root layout
βββ components/ # React components
β βββ ui/ # ShadCN UI components
β βββ providers/ # Context providers
β βββ (feature components) # Feature-specific components
βββ hooks/ # Custom React hooks
β βββ use-posts.ts # Post operations
β βββ use-auth.ts # Authentication
β βββ use-comments.ts # Comments
β βββ use-notifications.ts # Notifications
βββ lib/ # Utility libraries
β βββ prisma.ts # Prisma client
β βββ query-client.ts # React Query config
β βββ imagekit.ts # ImageKit integration
β βββ utils.ts # Helper functions
βββ types/ # TypeScript types
βββ prisma/ # Database schema
β βββ schema.prisma
βββ public/ # Static assets- Node.js 18.x or higher
- npm or yarn package manager
- MongoDB database (local or cloud)
- ImageKit account (for image uploads)
- Gmail account (for email sending)
Create a .env file in the root directory with the following variables:
# App Configuration
NEXT_PUBLIC_APP_URL=http://localhost:3000
# Database
# MongoDB connection string
# Format: mongodb://username:password@host:port/database?authSource=admin
DATABASE_URL="mongodb://localhost:27017/dev-blog"
# JWT Secret
# Generate a secure random string (minimum 256 characters recommended)
# You can generate one using: openssl rand -base64 32
JWT_SECRET="your-super-secret-jwt-key-change-this-in-production"
# Admin Configuration
# Email address for admin user (exposed to client for admin checks)
NEXT_PUBLIC_ADMIN_EMAIL="[email protected]"
# Email Configuration (for password reset)
# Gmail SMTP credentials
EMAIL_USER="[email protected]"
EMAIL_PASS="your-app-specific-password"
# ImageKit Configuration
# Get these from your ImageKit dashboard
NEXT_PUBLIC_IMAGEKIT_PUBLIC_KEY="your-imagekit-public-key"
IMAGEKIT_PRIVATE_KEY="your-imagekit-private-key"
NEXT_PUBLIC_IMAGEKIT_URL_ENDPOINT="https://ik.imagekit.io/your-imagekit-id"Local MongoDB:
DATABASE_URL="mongodb://localhost:27017/dev-blog"MongoDB Atlas (Cloud):
- Create account at MongoDB Atlas
- Create a cluster
- Get connection string from "Connect" button
- Replace
<password>with your database password
DATABASE_URL="mongodb+srv://username:[email protected]/dev-blog?retryWrites=true&w=majority"Generate a secure random string:
# Using OpenSSL
openssl rand -base64 32
# Or use Node.js
node -e "console.log(require('crypto').randomBytes(32).toString('base64'))"- Enable 2-Factor Authentication on your Google Account
- Go to Google App Passwords
- Create a new app password for "Mail"
- Use this password in
EMAIL_PASS
- Sign up at ImageKit
- Create a new project
- Get credentials from Dashboard β Developer Options
- Copy Public Key, Private Key, and URL Endpoint
git clone <repository-url>
cd dev-blognpm install-
Copy
.env.exampleto.env:cp .env.example .env
-
Fill in all required environment variables (see Environment Variables section)
-
Start MongoDB (if using local):
# macOS (using Homebrew) brew services start mongodb-community # Linux sudo systemctl start mongod # Windows # Start MongoDB service from Services panel
-
Generate Prisma Client:
npm run prisma:generate
-
Push Database Schema:
npm run prisma:push
This creates all tables/collections in your MongoDB database.
npm run devThe application will be available at http://localhost:3000
npm run devRuns the app in development mode with Turbopack for faster builds.
# Build the application
npm run build
# Start production server
npm start# Lint code
npm run lint
# Generate Prisma Client
npm run prisma:generate
# Push database schema changes
npm run prisma:push| Method | Endpoint | Description |
|---|---|---|
| POST | /api/auth/register |
Register new user |
| POST | /api/auth/login |
Login user |
| GET | /api/auth/validate |
Validate JWT token |
| GET | /api/auth/me |
Get current user |
| POST | /api/auth/forgot-password |
Request password reset |
| POST | /api/auth/reset-password |
Reset password with token |
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/posts |
Get all posts (with filters) |
| GET | /api/posts/[id] |
Get single post |
| POST | /api/posts |
Create new post |
| PUT | /api/posts/[id] |
Update post |
| DELETE | /api/posts/[id] |
Delete post |
| POST | /api/posts/[id]/like |
Like/unlike post |
| POST | /api/posts/[id]/helpful |
Mark post as helpful |
| POST | /api/posts/[id]/save |
Save post |
| POST | /api/posts/[id]/unsave |
Unsave post |
| POST | /api/posts/[id]/report |
Report post |
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/comments/post/[postId] |
Get comments for post |
| POST | /api/comments/post/[postId] |
Create comment |
| PUT | /api/comments/[id] |
Update comment |
| DELETE | /api/comments/[id] |
Delete comment |
| POST | /api/comments/[id]/like |
Like/unlike comment |
| POST | /api/comments/[id]/helpful |
Mark comment as helpful |
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/notifications |
Get user notifications |
| PUT | /api/notifications/[id]/mark-read |
Mark notification as read |
| PUT | /api/notifications/mark-all-read |
Mark all as read |
| DELETE | /api/notifications/[id] |
Delete notification |
| Method | Endpoint | Description |
|---|---|---|
| POST | /api/upload |
Upload image to ImageKit |
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/users/me/saved-posts |
Get saved posts |
| PUT | /api/users/me |
Update user profile |
Displays a post card with all interactions.
import PostCard from "@/components/PostCard";
<PostCard
post={{
id: "post-id",
title: "Post Title",
description: "Description",
// ... other post fields
}}
saved={false}
onUnsave={(postId) => {
/* handle unsave */
}}
onDelete={(postId) => {
/* handle delete */
}}
onLikeHelpfulUpdate={(postId, data) => {
/* handle update */
}}
/>;Props:
post: Post object with all post datasaved: Boolean indicating if post is savedonUnsave: Callback when post is unsavedonDelete: Callback when post is deletedonLikeHelpfulUpdate: Callback for like/helpful updates
Nested comment system with replies.
import CommentSection from "@/components/CommentSection";
<CommentSection
postId="post-id"
parentId="comment-id" // Optional: for nested replies
/>;Props:
postId: ID of the postparentId: Optional parent comment ID for nested replies
Reusable confirmation dialog (replaces window.confirm).
import { ConfirmDialog } from "@/components/ConfirmDialog";
const [showConfirm, setShowConfirm] = useState(false);
<ConfirmDialog
open={showConfirm}
onOpenChange={setShowConfirm}
title="Delete Post"
description="Are you sure you want to delete this post?"
confirmText="Delete"
cancelText="Cancel"
onConfirm={() => deletePost()}
variant="destructive" // "default" | "destructive"
/>;Reusable input dialog (replaces window.prompt).
import { InputDialog } from "@/components/InputDialog";
const [showDialog, setShowDialog] = useState(false);
<InputDialog
open={showDialog}
onOpenChange={setShowDialog}
title="Report Post"
description="Why are you reporting this post?"
placeholder="Enter reason..."
onConfirm={(value) => reportPost(value)}
type="textarea" // "text" | "textarea"
/>;All UI components are in components/ui/:
skeleton.tsx- Loading skeletonstoast.tsx- Toast notificationsdialog.tsx- Dialog componentalert-dialog.tsx- Alert dialog component
Fetch and manage posts with React Query.
import { usePosts, usePost, useCreatePost } from "@/hooks/use-posts";
// Fetch all posts
const { data: posts, isLoading } = usePosts({
tag: "react", // Optional filter
search: "error", // Optional search
});
// Fetch single post
const { data: post } = usePost("post-id");
// Create post
const createPost = useCreatePost();
createPost.mutate(formData);Available Hooks:
usePosts()- Get all postsusePost(id)- Get single postuseSavedPosts()- Get saved postsuseCreatePost()- Create post mutationuseUpdatePost()- Update post mutationuseDeletePost()- Delete post mutationuseLikePost()- Like/unlike mutation (optimistic)useMarkHelpful()- Mark helpful mutation (optimistic)useSavePost()- Save post mutationuseUnsavePost()- Unsave post mutation
Authentication hooks.
import { useAuth, useLogin, useLogout } from "@/hooks/use-auth";
// Check authentication
const { data: authData } = useAuth();
const user = authData?.user;
const isLoggedIn = !!user;
// Login
const login = useLogin();
login.mutate({ email: "[email protected]", password: "password" });
// Logout
const logout = useLogout();
logout.mutate();Available Hooks:
useAuth()- Get current useruseLogin()- Login mutationuseRegister()- Register mutationuseLogout()- Logout mutationuseUpdateProfile()- Update profile mutationuseRequestPasswordReset()- Request password resetuseResetPassword()- Reset password with token
Comment management hooks.
import { useComments, useCreateComment } from "@/hooks/use-comments";
// Fetch comments
const { data: comments } = useComments("post-id");
// Create comment
const createComment = useCreateComment();
createComment.mutate({
postId: "post-id",
content: "Comment text",
parentId: "parent-comment-id", // Optional: for replies
});Available Hooks:
useComments(postId)- Get comments for postuseCreateComment()- Create comment mutationuseUpdateComment()- Update comment mutationuseDeleteComment()- Delete comment mutationuseLikeComment()- Like/unlike comment (optimistic)
Notification hooks.
import {
useNotifications,
useMarkAllNotificationsRead,
} from "@/hooks/use-notifications";
// Fetch notifications
const { data: notifications } = useNotifications();
// Mark all as read
const markAllRead = useMarkAllNotificationsRead();
markAllRead.mutate();Available Hooks:
useNotifications()- Get notifications (auto-refetches every 2 minutes)useUnreadCount()- Get unread countuseMarkNotificationRead()- Mark single as readuseMarkAllNotificationsRead()- Mark all as readuseDeleteNotification()- Delete notification
Image upload hook.
import { useImageUpload } from "@/hooks/use-image-upload";
const { uploadImage, uploading, progress } = useImageUpload();
const handleUpload = async () => {
const result = await uploadImage(file, "posts");
if (result) {
console.log(result.url); // Image URL
console.log(result.fileId); // ImageKit file ID
}
};id: String (ObjectId)name: Stringemail: String (unique)password: String (hashed)country: String (optional)avatarUrl: String (optional)resetToken: String (optional)resetTokenExpiry: DateTime (optional)
id: String (ObjectId)title: Stringdescription: Stringcontent: StringcodeSnippet: String (optional)createdAt: DateTimetags: String[]imageUrl: String (optional)fileId: String (optional) - ImageKit file IDlikes: InthelpfulCount: IntauthorId: String (ObjectId) - Reference to User
id: String (ObjectId)content: StringcreatedAt: DateTimeavatarUrl: String (optional)imageUrl: String (optional)fileId: String (optional)postId: String (ObjectId)authorId: String (ObjectId)parentId: String (ObjectId, optional) - For nested comments
id: String (ObjectId)userId: String (ObjectId)type: String - 'like', 'helpful', 'comment', etc.message: StringisRead: BooleanpostId: String (optional)commentId: String (optional)fromUserId: String (ObjectId, optional)createdAt: DateTime
- User has many Posts, Comments, Likes, SavedPosts
- Post belongs to User, has many Comments, Likes, Helpfuls
- Comment belongs to User and Post, can have parent Comment (nested)
- Notification belongs to User
-
Copy Component Files:
# Copy PostCard and its dependencies components/PostCard.tsx components/PostHeader.tsx components/PostContent.tsx components/PostStats.tsx components/PostActionsBar.tsx components/PostDropdownMenu.tsx -
Copy Required Hooks:
hooks/use-posts.ts hooks/use-auth.ts
-
Copy UI Components:
components/ui/skeleton.tsx components/ui/toast.tsx components/ui/toaster.tsx hooks/use-toast.ts
-
Install Dependencies:
npm install @tanstack/react-query react-icons
-
Set Up React Query Provider:
// In your root layout import { QueryProvider } from "@/components/providers/query-provider"; export default function Layout({ children }) { return <QueryProvider>{children}</QueryProvider>; }
-
Use the Component:
import PostCard from "@/components/PostCard"; import { usePosts } from "@/hooks/use-posts"; function MyPage() { const { data: posts } = usePosts(); return ( <div> {posts?.map((post) => ( <PostCard key={post.id} post={post} /> ))} </div> ); }
-
Copy Files:
components/CommentSection.tsx components/CommentItem.tsx components/CommentInput.tsx components/CommentHeader.tsx components/CommentAvatar.tsx components/CommentActionsBar.tsx hooks/use-comments.ts hooks/use-image-upload.ts
-
Set Up API Endpoint:
Create
/api/comments/post/[postId]/route.tsin your Next.js app. -
Use the Component:
import CommentSection from "@/components/CommentSection"; <CommentSection postId="post-id" />;
-
Copy Files:
components/ConfirmDialog.tsx components/InputDialog.tsx components/ui/dialog.tsx components/ui/alert-dialog.tsx
-
Install Dependencies:
npm install @radix-ui/react-dialog @radix-ui/react-alert-dialog
-
Use Components:
import { ConfirmDialog } from "@/components/ConfirmDialog"; // Replace window.confirm <ConfirmDialog open={showConfirm} onOpenChange={setShowConfirm} title="Confirm Action" description="Are you sure?" onConfirm={handleConfirm} />;
-
Push code to GitHub
-
Import project in Vercel:
- Go to Vercel
- Click "New Project"
- Import your GitHub repository
-
Add Environment Variables:
- Go to Project Settings β Environment Variables
- Add all variables from
.envfile - Make sure
NEXT_PUBLIC_*variables are added
-
Configure Build Settings:
- Build Command:
npm run build - Output Directory:
.next - Install Command:
npm install
- Build Command:
-
Deploy:
- Click "Deploy"
- Vercel will automatically build and deploy
All environment variables from .env must be added to Vercel:
DATABASE_URLJWT_SECRETNEXT_PUBLIC_ADMIN_EMAILEMAIL_USEREMAIL_PASSNEXT_PUBLIC_IMAGEKIT_PUBLIC_KEYIMAGEKIT_PRIVATE_KEYNEXT_PUBLIC_IMAGEKIT_URL_ENDPOINTNEXT_PUBLIC_APP_URL(your Vercel URL)
- Use MongoDB Atlas (cloud database)
- Whitelist Vercel IP ranges (or use 0.0.0.0/0 for all)
- Update
DATABASE_URLin Vercel environment variables
Technologies: Next.js, React, TypeScript, MongoDB, Prisma, TanStack React Query, Tailwind CSS, ShadCN UI, ImageKit, JWT, bcrypt, Nodemailer
Features: Blog Platform, Developer Community, Code Sharing, Bug Tracking, Error Solutions, Nested Comments, Real-time Notifications, Image Upload, Authentication, Search & Filter
Concepts: Server-Side Rendering, Client-Side Rendering, Optimistic Updates, React Query, Caching, API Routes, Type Safety, Component Reusability, Responsive Design
Dev-Bug-Coder-Blog is a comprehensive, production-ready blogging platform that demonstrates modern web development practices. It showcases:
- Modern React Patterns: Hooks, Context, Custom Hooks
- State Management: React Query for server state
- Type Safety: Full TypeScript implementation
- Performance: Optimistic updates, caching, code splitting
- User Experience: Skeleton loaders, toast notifications, smooth animations
- Best Practices: Reusable components, centralized hooks, proper error handling
This project serves as an excellent learning resource for:
- Next.js App Router
- React Query patterns
- TypeScript in React
- MongoDB with Prisma
- Authentication flows
- Image handling
- Real-time features
Feel free to use this project repository and extend this project further!
If you have any questions or want to share your work, reach out via GitHub or my portfolio at https://arnob-mahmud.vercel.app/.
Enjoy building and learning! π
Thank you! π








