Full-Stack Developer Position Submission - Kapybara HQ
A production-ready blogging platform built with Next.js 16, featuring type-safe APIs, rich text editing, category management, and image uploads. Developed as part of the technical assessment for the Full-Stack Developer position at Kapybara HQ.
Deployed on Vercel with Neon PostgreSQL database
- Quick Start
- Tech Stack
- Features Implemented
- Project Structure
- Architecture Decisions
- Environment Setup
- Time Breakdown
- Node.js 18.x or higher
- npm or yarn
- Neon PostgreSQL account (free tier works)
- Cloudinary account (free tier works)
- Clone the repository cd blog-platform
- Install dependencies npm install
- Create .env file (see Environment Setup section) Edit .env with your credentials
- Push database schema to Neon npx drizzle-kit push
- Start development server npm run dev
- Open browser at http://localhost:3000 text
The application is now running locally with a connected PostgreSQL database.
| Technology | Version | Purpose |
|---|---|---|
| Next.js | 16.0.0 | Full-stack React framework with App Router |
| TypeScript | 5.0+ | Type safety across entire codebase |
| PostgreSQL | Latest | Relational database (hosted on Neon) |
| Drizzle ORM | Latest | Type-safe database ORM |
| tRPC | 11.0+ | End-to-end type-safe API layer |
| Zod | 3.22+ | Schema validation for tRPC |
| React Query | 5.0+ | Server state management (via tRPC) |
| Zustand | 4.4+ | Client-side global state |
| Tailwind CSS | 3.4+ | Utility-first CSS framework |
| Library | Purpose | Time Saved |
|---|---|---|
| shadcn/ui | Pre-built accessible components | ~3-4 hours |
| Tiptap | Rich text editor (WYSIWYG) | Better UX |
| Cloudinary | Image CDN and storage | ~2 hours |
| Material-UI Icons | Outlined icon set | ~1 hour |
| React Hot Toast | Toast notifications | ~30 min |
| next-themes | Dark mode support | ~1 hour |
- Neon PostgreSQL: Serverless, auto-scaling, generous free tier, instant setup
- Tiptap over Markdown: Better UX for content creators (trade-off: +2 hours dev time)
- shadcn/ui: Reduced UI development time by 70%
- Cloudinary: No server storage management, global CDN, free tier sufficient
- Monorepo: Single deployment, shared types, faster development
- Blog Post CRUD - Create, read, update, delete with full validation
- Category CRUD - Full category management system
- Multi-Category Assignment - Assign multiple categories per post
- Blog Listing Page - All posts with pagination and filters
- Individual Post View - Detailed post page with full content
- Category Filtering - Real-time filtering by category
- Responsive Navigation - Mobile drawer menu, desktop horizontal nav
- Clean Professional UI - Minimalist design, consistent spacing
Status: β All Priority 1 features fully implemented and tested
- Landing Page - 5 sections (Header, Hero, Features, Posts, CTA)
- Dashboard Page - Post management with search and filters
- Draft vs Published - Save drafts, publish when ready
- Loading States - Animated spinners throughout app
- Error Handling - User-friendly toast notifications
- Mobile Responsive - Fully responsive across all devices
- Rich Text Editor - Tiptap with formatting toolbar
Status: β All Priority 2 features fully implemented
- Full 5-Section Landing Page - Complete with all sections
- Search Functionality - Search by title and content
- Post Statistics - Word count and reading time
- Dark Mode - System-aware with manual toggle
- Image Upload - Cloudinary integration for post images
- Post Preview - Live preview before publishing
- Pagination - Server-side pagination for efficiency
- Advanced Editor Features - Bold, italic, lists, formatting
- SEO Meta Tags - Not implemented (time constraint)
Status: β 8/9 bonus features implemented
blog-platform/
βββ app/ # Next.js 16 App Router
β βββ page.tsx # Landing page (Hero + Features)
β βββ posts/
β β βββ page.tsx # All posts listing (with search/filter)
β β βββ new/page.tsx # Create new post
β β βββ [slug]/
β β βββ page.tsx # View individual post
β β βββ edit/page.tsx # Edit post
β βββ categories/
β β βββ manage/page.tsx # Category CRUD dashboard
β β βββ [slug]/page.tsx # Posts by category
β βββ api/trpc/[trpc]/route.ts # tRPC API handler
β βββ layout.tsx # Root layout (theme provider)
β βββ globals.css # Global Tailwind styles
β
βββ components/
β βββ forms/
β β βββ PostForm.tsx # Post create/edit form
β β βββ RichTextEditor.tsx # Tiptap editor wrapper
β β βββ PostPreview.tsx # Preview dialog
β βββ layout/
β β βββ Header.tsx # Navigation with mobile drawer
β β βββ Footer.tsx # Footer component
β β βββ ThemeToggle.tsx # Dark mode toggle
β βββ posts/
β β βββ PostCard.tsx # Post card component
β β βββ PostStats.tsx # Word count & reading time
β βββ ui/ # shadcn/ui components
β βββ button.tsx
β βββ card.tsx
β βββ dialog.tsx
β βββ sheet.tsx # Mobile drawer
β βββ ... (20+ components)
β
βββ server/
β βββ db/
β β βββ index.ts # Drizzle database connection
β β βββ schema.ts # Database schema definitions
β βββ trpc/
β βββ trpc.ts # tRPC initialization & context
β βββ routers/
β βββ index.ts # Root router (aggregates all)
β βββ post.ts # Post CRUD procedures
β βββ category.ts # Category CRUD procedures
β
βββ lib/
β βββ utils.ts # Utility functions (date format, etc.)
β
βββ trpc/
β βββ client.ts # tRPC React client setup
β
βββ .env # Environment variables (not in git)
βββ drizzle.config.ts # Drizzle ORM configuration
βββ tailwind.config.ts # Tailwind CSS config
βββ tsconfig.json # TypeScript configuration
- App Router Organization: Feature-based routing matches Next.js 16 conventions
- Server/Client Separation: Clear boundary between server code and client components
- Component Co-location: Related components grouped by feature
- Single tRPC Router: All API routes aggregated in one place for discoverability
- Flat Hierarchy: Minimal nesting for easier navigation
Decision: Full-stack in one Next.js project
Why:
- β Next.js 16 App Router is designed for full-stack
- β Shared TypeScript types between frontend/backend
- β Single deployment (Vercel optimized for this)
- β Faster development (no API versioning issues)
- β tRPC requires monorepo for type inference
Decision: Chose Neon over Supabase or local PostgreSQL
Why:
- β Serverless: Auto-scales to zero (cost-effective)
- β Instant setup: Database ready in 30 seconds
- β Generous free tier: 3GB storage, 100 hours compute
- β Branch database: Each git branch can have its own DB
- β Connection pooling: Built-in, no extra setup
Benefits of Neon:
- β Serverless (no server management)
- β Auto-scaling (scales to zero when idle)
- β Branch databases (test schema changes safely)
- β Fast setup (30 seconds)
Free Tier Limits: Perfect for demo projects
- Storage: 3 GB
- Compute: 100 hours/month
- Projects: Unlimited
Decision: Client-side upload to Cloudinary CDN
Why:
- β No server storage: Offload to CDN
- β Global delivery: Fast worldwide
- β Free tier: 10GB storage, 25GB bandwidth/month
- β Automatic optimization: WebP conversion, resizing
- β Simple setup: Unsigned upload preset
Benefits of Cloudinary:
- β No server storage needed
- β Automatic image optimization
- β Global CDN delivery
- β Free tier: 10GB storage
Alternative Considered: Next.js /public folder
- β Would require file system handling
- β No CDN benefits
- β Slower for users far from server
Create a .env file in the project root:
# Database Connection (Neon PostgreSQL)
DATABASE_URL="postgresql://username:[email protected]/neondb?sslmode=require"
# Cloudinary Configuration (for image uploads)
NEXT_PUBLIC_CLOUDINARY_CLOUD_NAME="your_cloud_name"
NEXT_PUBLIC_CLOUDINARY_UPLOAD_PRESET="blog_uploads"- Sign up at neon.tech (free, no credit card)
- Create project β Name it "blog-platform"
- Copy connection string from dashboard
- Paste into
.envasDATABASE_URL
- Sign up at cloudinary.com (free)
- Go to Settings β Upload β Upload Presets
- Click "Add upload preset"
- Configure:
- Preset name:
blog_uploads - Signing Mode: Select "Unsigned" (important!)
- Folder:
blog-platform/posts(optional) - Allowed formats:
jpg, png, webp - Max file size:
2 MB
- Preset name:
- Copy Cloud Name and Preset Name to
.env
npx drizzle-kit push
This creates all tables in your Neon database based on server/db/schema.ts.
Total Time Invested: ~15 hours
| Phase | Tasks | Time Spent |
|---|---|---|
| Day 1-2: Setup & Backend | Project init, Neon setup, Drizzle schema, tRPC routers, CRUD operations | ~4 hours |
| Day 3-4: Core Features | Post listing, individual post view, post form, category management, filtering | ~5 hours |
| Day 5-6: Priority 2 & UI | Landing page, rich text editor, mobile responsive, loading states, dark mode | ~4 hours |
| Day 7: Polish & Deployment | Image upload, search, pagination, bug fixes, README, Vercel deployment | ~2 hours |
| Decision | Time Saved |
|---|---|
| Used shadcn/ui components | ~3-4 hours |
| Neon (vs local PostgreSQL setup) | ~1 hour |
| Cloudinary (vs custom file handling) | ~2 hours |
| tRPC (vs REST API boilerplate) | ~2 hours |
| Total Saved: | ~8-9 hours |
This project was built as part of the technical assessment for the Full-Stack Developer position at Kapybara HQ.
This project was created for assessment purposes. All rights reserved.