Banner Background
Web App

Connectly

Connectly is a Twitter/X-style social networking platform that provides a robust server-driven architecture utilizing Next.js 15 App Router, React 19, Server Components, and Server Actions. It employs Clerk for authentication, PostgreSQL via Prisma ORM for relational persistent data, and UploadThing for image pipelines. The app layout is completely responsive and thematic, offering features like home feeds, user follower tracking, detailed chronological comments, and an indexed notification system.

Next JSReact JSTypescriptTailwind CSSPrismaPostgreSQLshadcn/uiShadcn UIZodZodClerkClerkpnpmGithubVercel
Connectly Preview

Overview

Connectly is a Twitter/X-style social network built on the Next.js App Router — posts, likes, comments, follows, and notifications, all driven by server actions instead of a separate REST or GraphQL layer. Authentication is fully delegated to Clerk, persistence is PostgreSQL via Prisma (with Accelerate for pooling and caching), and image uploads run through a typed UploadThing pipeline.

Connectly home feed with post composer, sidebar, and who-to-follow suggestions

The Connectly home feed — composer, chronological posts, and follow suggestions in the sidebar.

Server actions, not API routes

Nearly every mutation and query in Connectly — creating a post, liking, commenting, following, syncing a Clerk user — goes through a "use server" action in actions/, not a REST endpoint. The one exception is UploadThing, which requires its own route handler. Likes and comments are also written transactionally with their notification row via prisma.$transaction, so a like or comment can never exist without the notification that announces it.


Tech Stack

Next.js 15.5.2 (App Router, Turbopack, Server Components, Server Actions), React 19, TypeScript 5, and Node.js.

Clerk (@clerk/nextjs) for sign-in / sign-up / session management and middleware-based route protection, PostgreSQL, Prisma 6 as the ORM, and Prisma Accelerate (@prisma/extension-accelerate) for connection pooling and global query caching.

Tailwind CSS v4 (PostCSS), shadcn/ui on Radix UI primitives (Alert Dialog, Avatar, Dialog, Label, Scroll Area, Separator, Slot, Tabs), Lucide React icons, next-themes for dark/light/system mode, tw-animate-css, and class-variance-authority + tailwind-merge + clsx for className composition.

UploadThing (uploadthing, @uploadthing/react) for a typed file-upload pipeline, date-fns for relative timestamps, and react-hot-toast / sonner for toast notifications.

Feature Breakdown

Posts

Text posts with an optional single image, a reverse-chronological home feed, and server-side ownership checks on delete.

Likes & Comments

Toggleable likes with a unique (userId, postId) constraint, chronological comment threads, and atomic notification creation on both.

Follows

Follow / unfollow with a self-follow guard, a "who to follow" sidebar of up to 5 unfollowed users, and a FOLLOW notification on every follow.

Profiles

Dynamic /profile/[username] pages with bio, location, website, avatar, join date, tabs for posts vs. liked posts, and a custom not-found page.

Notifications

A dedicated /notifications feed for likes, comments, and follows, with skeleton loaders and a mark-as-read action, indexed by (userId, createdAt).

Image Uploads

A single reusable <ImageUpload /> component backed by an UploadThing route scoped to authenticated users, capped at 4 MB per post.

Architecture

Authentication Flow

Sign in via Clerk

The user authenticates through Clerk-hosted UI. clerkMiddleware() in middleware.ts protects every app page and /api/* route according to its matcher.

Sync into the local database

On first authenticated visit, syncUser() in actions/user.action.ts looks up a User row by clerkId and creates one if it's missing, copying name, email, username, and avatar from the Clerk profile.

Resolve the internal user id

Every subsequent server action calls getDbUserId() to resolve the current Clerk session into the internal database id used for all Prisma queries and mutations.

Request → Mutation → Cache Flow

Connectly deliberately skips a REST/GraphQL layer — Server Components read data directly, and client interactions call server actions that mutate through Prisma and then revalidate the affected route.

Why the transaction matters

Liking a post and creating the corresponding LIKE notification happen inside the same prisma.$transaction call, and the same is true for comments. This guarantees a notification can never be dropped or double-created if one half of the write fails.

Project Structure

notification.action.ts
post.action.ts
profile.action.ts
user.action.ts
layout.tsx
page.tsx
desktop-navbar.tsx
mobile-navbar.tsx
mode-toggle.tsx
navbar.tsx
sidebar.tsx
middleware.ts

Getting Started

pnpm install

# Generate the Prisma client (also runs automatically via postinstall)
pnpm prisma generate

# Apply database migrations
pnpm prisma migrate dev
pnpm dev

Starts the Next.js dev server with Turbopack at http://localhost:3000.

pnpm build
pnpm start

pnpm build produces a Turbopack production build; pnpm start serves it.

Frequently Asked Questions


See it in action

Explore the live feed, profiles, and notifications at connectly.naseemkhan.dev.