[{"data":1,"prerenderedAt":383},["ShallowReactive",2],{"/projects/whatsapp-clone":3},{"id":4,"title":5,"body":6,"description":297,"extension":364,"featured":365,"meta":366,"navigation":367,"path":368,"seo":369,"sortOrder":370,"stem":371,"tagline":372,"tags":373,"__hash__":382},"projects/projects/whatsapp-clone.md","WhatsApp Clone",{"type":7,"value":8,"toc":344},"minimark",[9,13,17,20,25,29,32,40,42,46,51,54,78,82,90,94,179,182,186,189,191,195,199,219,223,237,241,261,265,282,284,288,298,300,304,327,329,333],[10,11],"stack-chips",{":items":12},"[\"Nuxt 4\", \"Vue 3\", \"NestJS\", \"MongoDB\", \"WebSockets\", \"TypeScript\", \"Tailwind CSS\", \"Pinia\", \"Docker\", \"Playwright\", \"Vitest\", \"Jest\"]",[14,15],"media-carousel",{":items":16},"[{\"type\": \"image\", \"src\": \"/images/projects/whatsapp/group-chat-dark.png\", \"caption\": \"Group chat with multiple members in dark mode - real-time messaging with message status indicators.\"}, {\"type\": \"image\", \"src\": \"/images/projects/whatsapp/1on1-chat-dark.png\", \"caption\": \"Private 1:1 conversation in dark mode - end-to-end encrypted messaging with delivery and read receipts.\"}, {\"type\": \"video\", \"src\": \"/images/projects/whatsapp/1on1-chat-dark.mp4\", \"poster\": \"/images/projects/whatsapp/1on1-chat-dark-cover.png\", \"caption\": \"Real-time 1:1 encrypted messaging - two separate browser sessions exchanging messages instantly via WebSockets.\"}, {\"type\": \"video\", \"src\": \"/images/projects/whatsapp/group-chat-light.mp4\", \"poster\": \"/images/projects/whatsapp/group-chat-light-cover.png\", \"caption\": \"Group chat with 4 members - real-time message delivery, reply, forward and delete across simultaneous sessions.\"}, {\"type\": \"image\", \"src\": \"/images/projects/whatsapp/group-chat-light.png\", \"caption\": \"Group chat in light mode - full dark and light theme support.\"}, {\"type\": \"image\", \"src\": \"/images/projects/whatsapp/group-chat-info-light.png\", \"caption\": \"Group info panel - member management, admin controls, and group settings.\"}]",[18,19],"hr",{},[21,22,24],"h2",{"id":23},"overview","Overview",[26,27,28],"p",{},"A full-stack WhatsApp Web clone built as a showcase project. The app replicates the core experience of WhatsApp Web - real-time encrypted messaging between users, group chats, message status indicators, and a UI that mirrors the original application.",[26,30,31],{},"Built to demonstrate full-stack development, WebSocket communication, client-side cryptography, and professional testing practices including unit, integration, and end-to-end tests with a CI pipeline.",[33,34,37],"callout",{"title":35,"type":36},"Project Scope","info",[26,38,39],{},"This is a personal showcase project built entirely from scratch to demonstrate technical depth across the full stack - from cryptography and real-time communication to automated testing and CI pipelines.",[18,41],{},[21,43,45],{"id":44},"technical-highlights","Technical Highlights",[47,48,50],"h3",{"id":49},"end-to-end-encryption","End-to-End Encryption",[26,52,53],{},"The most technically demanding part of this project. Messages are encrypted client-side using the Web Crypto API before being sent to the server. The server never has access to plaintext message content.",[55,56,59],"decision",{"title":57,"tradeoff":58},"Client-side E2EE using Web Crypto API","Implementation complexity → true end-to-end encryption without trusting the server",[60,61,62,66,69,72,75],"ul",{},[63,64,65],"li",{},"RSA-OAEP key pairs generated per user on registration",[63,67,68],{},"Per-conversation AES-256 keys encrypted with each member's public key",[63,70,71],{},"Private keys encrypted with a user-defined passphrase using AES-GCM",[63,73,74],{},"Keys stored locally in IndexedDB - never sent to the server in plaintext",[63,76,77],{},"Recovery codes generated as alternative passphrase recovery options",[47,79,81],{"id":80},"real-time-communication","Real-Time Communication",[26,83,84,85,89],{},"WebSocket connections managed by a custom ",[86,87,88],"code",{},"WsClientManager"," service on the backend. Handles connection lifecycle, online/offline status, message delivery, and group broadcast without any third-party real-time service.",[47,91,93],{"id":92},"testing-161-tests","Testing - 161 Tests",[95,96,97,116],"table",{},[98,99,100],"thead",{},[101,102,103,107,110,113],"tr",{},[104,105,106],"th",{},"Suite",[104,108,109],{},"Tests",[104,111,112],{},"Tool",[104,114,115],{},"What's covered",[117,118,119,134,148,162],"tbody",{},[101,120,121,125,128,131],{},[122,123,124],"td",{},"Backend unit",[122,126,127],{},"52",[122,129,130],{},"Jest",[122,132,133],{},"Services, guards, strategies, WebSocket manager",[101,135,136,139,142,145],{},[122,137,138],{},"Frontend unit/nuxt",[122,140,141],{},"69",[122,143,144],{},"Vitest",[122,146,147],{},"Stores, composables, E2EE crypto",[101,149,150,153,156,159],{},[122,151,152],{},"Frontend E2E",[122,154,155],{},"40",[122,157,158],{},"Playwright",[122,160,161],{},"Auth, chat, messaging, block/unblock, profile, reply, forward, sign out",[101,163,164,170,175,177],{},[122,165,166],{},[167,168,169],"strong",{},"Total",[122,171,172],{},[167,173,174],{},"161",[122,176],{},[122,178],{},[26,180,181],{},"The most notable tests are the multi-browser E2E tests - two and three simultaneous Playwright browser contexts exchanging real-time encrypted messages, verifying the full stack from registration through encryption and delivery.",[47,183,185],{"id":184},"ci-pipeline","CI Pipeline",[26,187,188],{},"GitHub Actions runs all three test suites on every push. The E2E job starts MongoDB, the NestJS backend, and the Nuxt frontend via Docker Compose, then runs 40 Playwright tests in Chromium. Branch protection rules block merges unless all 161 tests pass.",[18,190],{},[21,192,194],{"id":193},"application-features","Application Features",[47,196,198],{"id":197},"messaging","Messaging",[60,200,201,204,207,210,213,216],{},[63,202,203],{},"Real-time 1:1 and group messaging via WebSockets",[63,205,206],{},"Message status indicators: Sent, Received, Read",[63,208,209],{},"Reply to messages with quoted preview",[63,211,212],{},"Forward messages to other chats",[63,214,215],{},"Delete messages",[63,217,218],{},"Message info (delivery and read receipts)",[47,220,222],{"id":221},"security-encryption","Security & Encryption",[60,224,225,228,231,234],{},[63,226,227],{},"End-to-end encryption using the Web Crypto API",[63,229,230],{},"JWT authentication with HTTP-only cookies",[63,232,233],{},"Session token validation on every authenticated request",[63,235,236],{},"Rate limiting on all API endpoints",[47,238,240],{"id":239},"users-chats","Users & Chats",[60,242,243,246,249,252,255,258],{},[63,244,245],{},"Registration with passphrase setup and recovery codes",[63,247,248],{},"Profile management: name and about",[63,250,251],{},"Block and unblock users",[63,253,254],{},"Online/offline status with last seen timestamps",[63,256,257],{},"1:1 and group chats with admin management",[63,259,260],{},"Add and remove group members",[47,262,264],{"id":263},"ui","UI",[60,266,267,270,273,276,279],{},[63,268,269],{},"Dark and light mode",[63,271,272],{},"Emoji picker",[63,274,275],{},"Message search within chats",[63,277,278],{},"Typing indicators",[63,280,281],{},"Unread message counts",[18,283],{},[21,285,287],{"id":286},"architecture","Architecture",[289,290,295],"pre",{"className":291,"code":293,"language":294},[292],"language-text","Client (Browser)\n├── Nuxt 4 + Vue 3 + Pinia\n├── Web Crypto API - E2EE\n├── IndexedDB - private key storage\n└── WebSocket client\n        │ HTTP / WebSocket\nNestJS Backend\n├── REST API (auth, users, chats, messages)\n├── WebSocket Gateway (real-time events)\n├── JWT + Cookie authentication\n└── Rate limiting\n        │ Mongoose\nMongoDB\n","text",[86,296,293],{"__ignoreMap":297},"",[18,299],{},[21,301,303],{"id":302},"what-this-project-demonstrates","What This Project Demonstrates",[60,305,306,309,312,315,318,321,324],{},[63,307,308],{},"Full-stack TypeScript development across NestJS and Nuxt 4",[63,310,311],{},"Client-side cryptography using the Web Crypto API - RSA-OAEP + AES-256",[63,313,314],{},"Real-time WebSocket communication with custom connection management",[63,316,317],{},"Comprehensive testing: 161 tests across unit, integration, and E2E",[63,319,320],{},"Multi-browser E2E testing with Playwright - simultaneous sessions verifying real-time encrypted delivery",[63,322,323],{},"CI pipeline with Docker Compose running the full stack in GitHub Actions",[63,325,326],{},"Professional engineering practices: branch protection, conventional commits, documented architecture",[18,328],{},[21,330,332],{"id":331},"links","Links",[60,334,335],{},[63,336,337],{},[338,339,343],"a",{"href":340,"rel":341},"https://github.com/vortizz/whatsapp",[342],"nofollow","GitHub Repository",{"title":297,"searchDepth":345,"depth":345,"links":346},2,[347,348,355,361,362,363],{"id":23,"depth":345,"text":24},{"id":44,"depth":345,"text":45,"children":349},[350,352,353,354],{"id":49,"depth":351,"text":50},3,{"id":80,"depth":351,"text":81},{"id":92,"depth":351,"text":93},{"id":184,"depth":351,"text":185},{"id":193,"depth":345,"text":194,"children":356},[357,358,359,360],{"id":197,"depth":351,"text":198},{"id":221,"depth":351,"text":222},{"id":239,"depth":351,"text":240},{"id":263,"depth":351,"text":264},{"id":286,"depth":345,"text":287},{"id":302,"depth":345,"text":303},{"id":331,"depth":345,"text":332},"md",false,{},true,"/projects/whatsapp-clone",{"title":5,"description":297},8,"projects/whatsapp-clone","Full-stack real-time messaging app with end-to-end encryption, group chats, and 161 automated tests",[374,375,376,377,378,379,380,381,158],"Vue.js","Nuxt.js","NestJS","MongoDB","WebSockets","TypeScript","Tailwind CSS","Docker","Sy2df_MOfOYZsjKvWR9hKdKiDEH89jMDxh-xZ8Vi_Jw",1778044213252]