Blogging Website is a modern, full-stack blogging application built with Next.js, tRPC, Drizzle ORM, and PostgreSQL.
It provides a complete, type-safe experience from the database to the client, featuring user authentication, a rich text editor, and post management.
https://hemant-blogging-website.vercel.app/
- User Authentication — Secure sign-up and sign-in using JWTs stored in
httpOnlycookies. - Post Management (CRUD) — Authenticated users can create, read, update, and delete their own posts.
- Public Post Viewing — All published posts are visible to the public on a paginated main feed.
- Single Post View — Users can view full post content on a dedicated page.
- Draft & Publish System — Users can save posts as drafts or publish them.
- Profile Page — Dedicated page (
/u/[username]) showing all of a user’s posts (published + drafts). - Post Categories — Posts support multiple categories (many-to-many relationship).
- Dynamic Category Creation — Create new categories directly from the post editor.
- Image Uploads — The rich text editor supports Cloudinary image uploads.
- Recent Posts — Homepage highlights the 3 most recently updated posts.
- Responsive Design — Fully responsive and mobile-friendly.
- Pagination — Efficiently handles a large number of posts.
- Category Filtering — Filter the main feed by category.
- Loading Skeletons — Shown while data is being fetched.
- Toasts/Notifications — Feedback for saving, publishing, and errors.
| Layer | Technology |
|---|---|
| Framework | Next.js (App Router) |
| API | tRPC (end-to-end type safety) |
| ORM | Drizzle ORM |
| Database | PostgreSQL (via Vercel Postgres) |
| Auth | JWT (stored in httpOnly cookies) |
| Styling | Tailwind CSS |
| UI Components | shadcn/ui |
| Validation | Zod (via tRPC + react-hook-form) |
| Image Hosting | Cloudinary |
| Language | TypeScript |
Follow these steps to get the project running locally.
git clone https://github.com/Hemantpali/blogging_website
cd blogging_websitenpm installCreate a .env file in the root of your project and add:
# 1. Database URL
# Get this from your PostgreSQL provider (e.g., Neon)
DATABASE_URL="postgres://..."
# 2. JWT Secret
# Generate one here: https://generate-secret.now.sh/32
JWT_SECRET="YOUR_SUPER_SECRET_JWT_KEY"
# 3. Cloudinary (for image uploads)
# Get these from your Cloudinary dashboard
NEXT_PUBLIC_CLOUD_NAME="YOUR_CLOUDINARY_CLOUD_NAME"
NEXT_PUBLIC_UPLOAD_PRESET="YOUR_CLOUDINARY_UPLOAD_PRESET"
NEXT_PUBLIC_DEPLOYED_URL="YOUR_DEPLOYED_URL"This project uses Drizzle ORM. Push your schema to the database:
npx drizzle-kit pushnpm run devNow open http://localhost:3000 in your browser
This project does not include a dedicated seed script.
To seed manually:
- Sign up for a new user account.
- Go to Create Post.
- Under Select categories, create categories (e.g., “Tech”, “Lifestyle”, “Productivity”).
- Create and publish a few posts.
KapyBlog uses tRPC for end-to-end type safety.
Routers are modularized by feature and combined in src/trpc/routers/_app.ts.
src/trpc/init.ts— Initializes tRPC and definesprotectedProceduremiddleware to enforce authentication.
src/trpc/routers/_app.ts— Merges all sub-routers.
-
authRouter(src/modules/auth/server/procedures.ts)create— Handles new user sign-up.login— Handles user sign-in.
-
postRouter(src/modules/posts/server/procedures.ts)create/save— Upsert logic (create or update post; handles publish/draft).remove— Delete a post (protected).getAll— Fetch all published posts (paginated).getRecent— Get 3 most recent posts.getOne— Get a single post by slug.
-
categoriesRouter(src/modules/categories/server/procedures.ts)create— Create new category (protected).list— List all available categories.
-
profileRouter(src/modules/profile/server/procedures.ts)getMany— Fetch all posts (published + drafts) for logged-in user.
- Uses JWTs stored in httpOnly cookies — secure, simple, and prevents XSS.
- Trade-off: No built-in session invalidation (requires DB-backed sessions for that).
- Custom rich text editor using
document.execCommand. - Fast to build, supports bold/italic and image uploads.
- Trade-off:
execCommandis deprecated; better long-term option is TipTap or Lexical.
- Generated client-side before creation (simplifies UI flow).
- Trade-off: Relies on random strings instead of title-based slugs checked for uniqueness.
- Drizzle ORM chosen for its lightweight nature, speed, and excellent type-safety — pairs well with tRPC.