Portfolio
Loading Shiham's portfolio
Preparing the latest projects, skills, and contact paths with a quiet production-ready polish.
Initializing portfolio
Projects · Skills · Contact
Portfolio
Preparing the latest projects, skills, and contact paths with a quiet production-ready polish.
Initializing portfolio
Projects · Skills · Contact
Built as a personal, solo project, CodeVerse gives developers a focused space to exchange programming knowledge. It combines community Q&A workflows with searchable tags, public profiles, reputation signals, and personal question collections.
Users can register with credentials or sign in through Google or GitHub, publish MDX-formatted questions and answers, vote on content, edit or delete their own questions, delete their answers, filter and search discussions, and browse community profiles. A Groq-powered assistant can generate markdown answer drafts inside the editor.
The application is implemented as a Next.js App Router monolith using Server Components, client components, Server Actions, and route handlers. MongoDB persistence is managed through Mongoose models and transactions for multi-collection changes, with Zod validation, NextAuth sessions, and centralized error handling.
Related projects
Jan 2026 – Jun 2026
A multi-vendor commerce platform connecting buyers, sellers, and administrators through dedicated storefronts, dashboards, payments, chat, and personalized discovery.
CodeVerse is a full-stack Next.js App Router application that combines React UI, Server Components, Server Actions, and route handlers in one deployable codebase. Mongoose models persist users, accounts, questions, answers, tags, votes, collections, and interactions in MongoDB, while NextAuth manages sessions and Groq provides AI-generated answer drafts.
A community-driven Q&A platform for developers — ask questions, share knowledge, and grow together.
CodeVerse is a full-stack developer Q&A web application inspired by Stack Overflow. It was built as a hands-on project to explore and deeply understand modern Next.js patterns — including the App Router, Server Actions, Server Components, and API Routes — alongside tools like NextAuth.js, MongoDB/Mongoose, and the Vercel AI SDK.
Users can ask and answer programming questions, vote on content, bookmark questions, follow tags, and discover developer jobs. An integrated AI assistant powered by Groq (Llama 3.1) can generate markdown-formatted answers to help get the conversation started.
| Action | Performer | Content Author |
|---|---|---|
| Upvote a post | +2 | +10 |
| Downvote a post | -1 | -2 |
| Post a question | — | +5 |
| Post an answer | — | +10 |
| Delete a question | — | -5 |
| Delete an answer | — | -10 |
| Category | Technology |
|---|---|
| Framework | Next.js 15 (App Router) |
| Language | TypeScript 5 |
| UI Library | React 19 |
| Styling | Tailwind CSS 4, tailwind-merge, tailwindcss-animate |
| Component Primitives | Radix UI (Alert Dialog, Avatar, Dialog, Dropdown, Label, Select, Slot, Tabs) |
| Component Library | shadcn/ui |
| Icons | Lucide React, Radix Icons, Devicon (CDN) |
| Rich Text Editor | @mdxeditor/editor, next-mdx-remote, Bright (syntax highlighting) |
| Database | MongoDB (Atlas) |
| ODM | Mongoose 8 |
CodeVerse uses MongoDB with Mongoose ODM. All models use timestamps (createdAt, updatedAt).
All REST endpoints live under app/api/. Server Actions in lib/actions/ handle the majority of mutations.
| Method | Endpoint | Description |
|---|---|---|
GET/POST | /api/auth/[...nextauth] | NextAuth.js authentication handler |
POST | /api/auth/signin-with-oauth | OAuth sign-in — create or link account |
POST | /api/ai/answers | Generate an AI answer via Groq (Llama 3.1) |
GET/POST | /api/accounts | List / create accounts |
GET/PUT/DELETE | /api/accounts/[id] | Get / update / delete a specific account |
packageManager in package.json)Clone the repository
git clone https://github.com/theShihamAhamed/CodeVerse-app.git
cd CodeVerse-app
Install dependencies
npm install
Create a .env.local file in the root of the project and populate it with the following:
# ─── MongoDB ──────────────────────────────────────────────────────────────────
MONGODB_URI=mongodb+srv://<username>:<password>@<cluster>.mongodb.net/?retryWrites=true&w=majority
# ─── NextAuth ─────────────────────────────────────────────────────────────────
# Generate with: openssl rand -base64 32
AUTH_SECRET=your_nextauth_secret
# ─── OAuth Providers ──────────────────────────────────────────────────────────
# Google: https://console.developers.google.com/
AUTH_GOOGLE_ID=your_google_client_id
AUTH_GOOGLE_SECRET=your_google_client_secret
# GitHub: https://github.com/settings/developers
AUTH_GITHUB_ID=your_github_client_id
AUTH_GITHUB_SECRET=your_github_client_secret
# ─── Groq (AI Answer Generation) ──────────────────────────────────────────────
# https://console.groq.com/
GROQ_API_KEY=your_groq_api_key
# ─── RapidAPI (Job Listings) ──────────────────────────────────────────────────
# https://rapidapi.com/letscrape-6bRBa3QguO5/api/jsearch
NEXT_PUBLIC_RAPID_API_KEY=your_rapidapi_key
# ─── App URL ──────────────────────────────────────────────────────────────────
NEXTAUTH_URL=http://localhost:3000
Note: The
NEXT_PUBLIC_prefix onRAPID_API_KEYmakes it accessible in the browser. All other secrets stay server-side only.
npm run dev
Open http://localhost:3000 in your browser. The app uses Turbopack (next dev --turbo) for fast refresh.
Other scripts:
npm run build # Production build
npm run start # Start production server
npm run lint # Run ESLint
All data mutations (creating questions, voting, bookmarking) are implemented as Next.js Server Actions in lib/actions/. Each action goes through a central action() handler in lib/handlers/action.ts that validates the payload against a Zod schema and optionally checks for an authenticated session before executing.
Operations that touch multiple collections atomically — such as creating a question (which also creates/upserts tags and TagQuestion junction records), deleting a question (which cascades to answers, votes, and collections), and voting (which updates the Vote document and the counter on the Question/Answer) — all use Mongoose transactions to guarantee consistency.
The "Recommended" feed works by:
view, upvote, bookmark, or post.Every meaningful user action triggers createInteraction(), which records an Interaction document and calls updateReputation(). This uses User.bulkWrite() to atomically update both the performer's and the content author's reputation in a single database round-trip.
The /api/ai/answers route receives the question title, MDX content, and an optional user draft. It uses the Vercel AI SDK (generateText with @ai-sdk/groq) to call the Llama 3.1 8B Instant model on Groq, instructing it to produce a concise, markdown-formatted response that incorporates the user's draft if it is correct.
A unified handleError() function in lib/handlers/error.ts normalises any error — whether a custom RequestError subclass (Validation, NotFound, Forbidden, Unauthorized), a ZodError, or an unknown runtime error — into a consistent { success: false, error: { message, details } } shape for both API responses (NextResponse.json) and Server Action returns.
NextAuth.js v5 is configured in auth.ts with a credentials provider (email + bcrypt) and google/github OAuth providers. The middleware.ts file re-exports the NextAuth auth function directly as the middleware, protecting routes transparently. An Account model separates auth-provider details from the User model, allowing one user to have multiple linked providers.
The case-study preview is collapsed. Activate the button to make the full content available.
Aug 2025 – Oct 2025
A marketplace for verified Sri Lankan homestays, supporting guest bookings, host onboarding, admin approval, and test-mode payments.
Jan 2026 – Jan 2026
A hands-on DevOps playground for developers learning to containerize an Express API and run replicated workloads on Kubernetes with Docker and Minikube.
| Authentication | NextAuth.js v5 (Credentials + OAuth) |
| Password Hashing | bcryptjs |
| AI / LLM | Vercel AI SDK (ai, @ai-sdk/groq) — Llama 3.1 8B Instant via Groq |
| Form Handling | react-hook-form, @hookform/resolvers |
| Validation | Zod |
| Date Formatting | Day.js |
| URL State | query-string |
| Slug Generation | slugify |
| Logging | Pino, pino-pretty |
| Linting / Formatting | ESLint 9, Prettier |
| Deployment | Vercel |
CodeVerse-app/
├── app/
│ ├── (auth)/ # Authentication route group
│ │ ├── layout.tsx # Centered card layout with brand imagery
│ │ ├── sign-in/page.tsx
│ │ └── sign-up/page.tsx
│ ├── (root)/ # Main application route group
│ │ ├── layout.tsx # Navbar + LeftSidebar + RightSidebar shell
│ │ ├── page.tsx # Home — all questions feed
│ │ ├── ask-question/page.tsx
│ │ ├── questions/[id]/
│ │ │ ├── page.tsx # Question detail with answers
│ │ │ └── edit/page.tsx
│ │ ├── collection/page.tsx # Saved questions (auth-gated)
│ │ ├── community/page.tsx # All users
│ │ ├── tags/
│ │ │ ├── page.tsx # All tags
│ │ │ └── [id]/page.tsx # Questions by tag
│ │ ├── profile/
│ │ │ ├── [id]/page.tsx # Public profile
│ │ │ └── edit/page.tsx # Edit own profile (auth-gated)
│ │ └── jobs/page.tsx # Developer job listings
│ ├── api/
│ │ ├── auth/[...nextauth]/route.ts # NextAuth handler
│ │ ├── auth/signin-with-oauth/route.ts
│ │ ├── ai/answers/route.ts # Groq AI answer generation
│ │ ├── accounts/route.ts
│ │ ├── accounts/[id]/route.ts
│ │ ├── accounts/provider/route.ts
│ │ ├── users/route.ts
│ │ ├── users/[id]/route.ts
│ │ └── users/email/route.ts
│ ├── fonts/ # Local Inter & Space Grotesk variable fonts
│ ├── globals.css # Tailwind base + CSS custom properties
│ ├── layout.tsx # Root layout (SessionProvider, ThemeProvider)
│ └── not-found.tsx
│
├── components/
│ ├── cards/ # QuestionCard, AnswerCard, TagCard, UserCard, JobCard
│ ├── filters/ # CommonFilter, HomeFilter
│ ├── forms/ # AuthForm, QuestionForm, ProfileForm, SocialAuthForm, AnswerForm
│ ├── navigation/
│ │ ├── navbar/ # Top navigation bar + GlobalSearch
│ │ ├── LeftSidebar.tsx # Main nav links
│ │ └── RightSidebar.tsx # Hot questions + Popular tags
│ ├── search/ # LocalSearch, GlobalSearch, GlobalResult
│ ├── answers/ # AllAnswers component
│ ├── questions/ # SaveQuestion, HotQuestions
│ ├── votes/ # Votes (upvote/downvote UI)
│ ├── user/ # Stats, ProfileLink
│ ├── editor/ # MDXEditor wrapper
│ ├── ui/ # shadcn/ui primitives
│ ├── DataRenderer.tsx # Generic success/error/empty state renderer
│ ├── Pagination.tsx
│ ├── Metric.tsx
│ └── UserAvatar.tsx
│
├── database/
│ ├── index.ts # Barrel export for all models
│ ├── user.model.ts
│ ├── account.model.ts
│ ├── question.model.ts
│ ├── answer.model.ts
│ ├── tag.model.ts
│ ├── tag-question.model.ts # Many-to-many junction
│ ├── vote.model.ts
│ ├── collection.model.ts # Bookmarks
│ └── interaction.model.ts # Drives recommendations & reputation
│
├── lib/
│ ├── actions/ # Next.js Server Actions
│ │ ├── auth.action.ts
│ │ ├── question.action.ts
│ │ ├── answer.action.ts
│ │ ├── vote.action.ts
│ │ ├── collection.action.ts
│ │ ├── tag.action.ts
│ │ ├── user.action.ts
│ │ ├── general.action.ts # Global search
│ │ ├── interaction.action.ts # Reputation updates
│ │ └── job.action.ts # RapidAPI JSearch
│ ├── handlers/
│ │ ├── action.ts # Auth + validation middleware for Server Actions
│ │ ├── error.ts # Unified error formatter (API & server)
│ │ └── fetch.ts # Typed fetch wrapper
│ ├── http-errors.ts # Custom error classes
│ ├── mongoose.ts # Singleton MongoDB connection
│ ├── validations.ts # Zod schemas
│ ├── utils.ts # Helpers (cn, formatNumber, getTimestamp, etc.)
│ ├── url.ts # URL query string helpers
│ ├── api.ts # Client-side API helpers
│ └── logger.ts # Pino logger setup
│
├── constants/
│ ├── routes.ts # Typed route map
│ ├── filters.ts # Filter option arrays per page
│ ├── states.ts # Empty-state configs
│ ├── techMap.ts # Technology → Devicon class mapping
│ └── notFound.ts
│
├── context/
│ └── Theme.tsx # next-themes ThemeProvider wrapper
│
├── types/
│ ├── global.d.ts # Shared interfaces (Question, Answer, User, Job, etc.)
│ └── action.d.ts # Server Action parameter types
│
├── public/
│ ├── icons/ # SVG icon set
│ └── images/ # Logos, auth background, illustrations
│
├── auth.ts # NextAuth.js configuration
├── middleware.ts # Route protection via NextAuth middleware
├── next.config.ts
├── tailwind.config.ts
└── tsconfig.json
┌─────────────────────────────────────────────────────────────────────┐
│ User │ Account │
│────────────────────── │────────────────────────────────────── │
│ name: String │ userId: ObjectId → User │
│ username: String (uniq) │ name: String │
│ email: String (uniq) │ image: String? │
│ bio: String? │ password: String? (hashed) │
│ image: String? │ provider: String (credentials/google/ │
│ location: String? │ github) │
│ portfolio: String? │ providerAccountId: String │
│ reputation: Number = 0 └────────────────────────────────────── │
└─────────────────────────────────────────────────────────────────────┘
┌───────────────────────────────────────────────────────────────────────────┐
│ Question │ Tag │
│────────────────────────────── │────────────────────────────── │
│ title: String │ name: String (unique) │
│ content: String (MDX) │ questions: Number = 0 │
│ tags: ObjectId[] → Tag └────────────────────────────── │
│ author: ObjectId → User │
│ views: Number = 0 TagQuestion (junction) │
│ upvotes: Number = 0 ───────────────────────── │
│ downvotes: Number = 0 tag: ObjectId → Tag │
│ answers: Number = 0 question: ObjectId → Question │
└───────────────────────────────────────────────────────────────────────────┘
┌────────────────────────────────────────────────────────────────────────────┐
│ Answer │ Vote │
│──────────────────────────── │──────────────────────────────────────── │
│ author: ObjectId → User │ author: ObjectId → User │
│ question: ObjectId → Question │ actionId: ObjectId (question or answer) │
│ content: String (MDX) │ actionType: "question" | "answer" │
│ upvotes: Number = 0 │ voteType: "upvote" | "downvote" │
│ downvotes: Number = 0 └──────────────────────────────────────── │
└────────────────────────────────────────────────────────────────────────────┘
┌────────────────────────────────────────────────────────────────────────────┐
│ Collection (Bookmark) │ Interaction │
│──────────────────────────── │──────────────────────────────────────── │
│ author: ObjectId → User │ user: ObjectId → User │
│ question: ObjectId → Question │ action: "view"|"upvote"|"downvote"| │
│ │ "bookmark"|"post"|"edit"| │
│ │ "delete"|"search" │
│ │ actionId: ObjectId │
│ │ actionType: "question" | "answer" │
└────────────────────────────────────────────────────────────────────────────┘
GET | /api/accounts/provider | Look up an account by provider + providerAccountId |
GET/POST | /api/users | List / create users |
GET/PUT/DELETE | /api/users/[id] | Get / update / delete a specific user |
GET | /api/users/email | Look up a user by email address |