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.
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
Getting Started
pnpm install
# Generate the Prisma client (also runs automatically via postinstall)
pnpm prisma generate
# Apply database migrations
pnpm prisma migrate devpnpm devStarts the Next.js dev server with Turbopack at http://localhost:3000.
pnpm build
pnpm startpnpm 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.
