Skip to content

MeMoElprince/poking-app

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

151 Commits
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

πŸ’¬ Poking App

A real-time, passwordless chat platform β€” add friends by email and start talking instantly.

Live Demo Β  React Node.js Socket.IO MongoDB


Table of Contents


Overview

Poking App is a full-stack chat application. There are no passwords β€” you sign in with your email and a one-time code (OTP). Once in, you add friends by their email address and chat in real time, with typing indicators, online presence, and read receipts.

🌐 Live: client on pokingapp.vercel.app · API on Render


Features

  • πŸ” Passwordless auth β€” email β†’ 6-digit OTP β†’ JWT session
  • πŸ’¬ Real-time messaging over WebSockets (Socket.IO)
  • ✍️ Typing indicators β€” see when a friend is typing
  • 🟒 Online presence & last seen β€” live status dots, "last seen …"
  • βœ“βœ“ Read receipts β€” single tick (sent) β†’ double tick (read)
  • ⚑ Optimistic send β€” messages appear instantly with a sending/sent/read state
  • πŸ‘₯ Friend system β€” send / accept / decline requests, delete friends
  • πŸ”Ž Search your chats, with most-recent conversations first
  • πŸͺͺ Profile page β€” display name, username, avatar
  • 🎨 Polished UX β€” skeleton loaders, smooth animations, dark theme, responsive layout

Coming soon

  • User avatars / photo upload
  • File & image sharing in chat
  • Group chats

Tech Stack

Layer Technologies
Frontend React 18, Vite, Tailwind CSS, Socket.IO Client, Framer Motion, React-Toastify, React-Icons, Moment, js-cookie
Backend Node.js, Express, Socket.IO, Mongoose (MongoDB), JSON Web Tokens
Email (OTP) Brevo HTTP API (no SMTP β€” works on hosts that block SMTP)
Security/Perf Helmet, CORS, express-rate-limit, express-mongo-sanitize, compression (gzip)
Hosting Vercel (client), Render (API), MongoDB Atlas (database)

Architecture

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    HTTPS / WSS    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”      β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚   Client   β”‚ ◀───────────────▢ β”‚       Express API        β”‚ ◀──▢ β”‚ MongoDB Atlasβ”‚
β”‚ (React/Viteβ”‚   REST + Socket   β”‚  + Socket.IO (one server)β”‚      β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
β”‚  Vercel)   β”‚                   β”‚         Render           β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                   β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                                               β”‚ HTTPS
                                               β–Ό
                                       β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                                       β”‚  Brevo (OTP)  β”‚
                                       β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
  • A single HTTP server serves both the REST API and the Socket.IO connection.
  • Auth is JWT: issued after OTP verification, sent as a Bearer token for REST and in the socket handshake (auth.token) for the websocket β€” the server rejects unauthenticated sockets and derives the user id from the token (clients can't spoof a sender).
  • Online presence is tracked in memory (single instance). See Security Notes.

Getting Started

Prerequisites

  • Node.js β‰₯ 18
  • A MongoDB connection string (e.g. MongoDB Atlas)
  • A free Brevo account + API key and a verified sender email

1. Clone

git clone https://github.com/MeMoElprince/poking-app.git
cd poking-app

2. Server

cd server
npm install
cp env.example .env     # then fill in the values (see below)
npm run dev             # development (nodemon)
# npm start             # production (node server.js)

3. Client

cd client
npm install
# create client/.env with VITE_API_URL (see below)
npm run dev             # Vite dev server
npm run build           # production build β†’ dist/

ℹ️ The server reads its config from server/.env (copied from env.example).


Environment Variables

server/.env

Variable Required Description
DATABASE βœ… MongoDB connection string (use <PASSWORD> placeholder for the password)
DATABASE_PASSWORD βœ… DB password, substituted into DATABASE at startup
JWT_SECRET βœ… Secret used to sign JWTs
JWT_EXPIRES_IN βœ… Token lifetime, e.g. 90d
BREVO_API_KEY βœ… Brevo API key for sending OTP emails
BREVO_SENDER_EMAIL βœ… A sender address verified in Brevo
BREVO_SENDER_NAME β€” Display name for the sender (defaults to PokingApp)
CLIENT_URL β€” Restrict CORS to this origin (defaults to *)
PORT β€” Server port (defaults to 2000, or Render's assigned port)

client/.env

Variable Required Description
VITE_API_URL βœ… Base URL of the API, with a trailing slash β€” e.g. https://your-api.onrender.com/

Available Scripts

Server (/server)

Script Action
npm start Run in production (node server.js)
npm run dev Run with nodemon, NODE_ENV=development
npm run prod Run with nodemon, NODE_ENV=production

Client (/client)

Script Action
npm run dev Start the Vite dev server
npm run build Build for production into dist/
npm run preview Preview the production build
npm run lint Run ESLint

Real-time Events

All socket connections require a valid JWT in the handshake: io(url, { auth: { token } }).

Client β†’ Server

Event Payload Purpose
join-room roomId Subscribe to a chat room and load its latest messages
load-older { room, before } Fetch older messages before a timestamp (pagination)
send-message { room, message }, ack Persist & broadcast a message; ack returns the saved doc
mark-read { room } Mark the room's incoming messages as read
typing / stop-typing { room } Relay typing state to the other participant
get-friends userId Ask the server to push a fresh friends list to a user
friend-request-sent / number-friend-request userId Notify a user of a new/changed friend request

Server β†’ Client

Event Payload Purpose
presence:init [userId] Snapshot of who is currently online
presence { userId, online, lastSeen? } Live online/offline updates
get-messages [message] A room's latest messages
older-messages { room, messages } Paged older messages
receive-message message A new message in the open room
message-notification { room, message, senderId } New-message hint for the chat list (preview / unread)
messages-read { room } Recipient read your messages β†’ flip ticks to βœ“βœ“
typing / stop-typing { room, userId } The other participant's typing state

Project Structure

poking-app/
β”œβ”€β”€ client/                  # React + Vite frontend
β”‚   └── src/
β”‚       β”œβ”€β”€ Components/       # Pages, RootComponents (Left/Right/Sidebar), UiComponents
β”‚       β”œβ”€β”€ Store/            # Context (User, Friends, Presence, BackDrop), socket.js, urls.js
β”‚       └── ...
└── server/                  # Express + Socket.IO backend
    β”œβ”€β”€ app.js               # Express app, middleware & all Socket.IO handlers
    β”œβ”€β”€ server.js            # DB connection + HTTP server bootstrap
    β”œβ”€β”€ controllers/         # auth, user, message, global error handler
    β”œβ”€β”€ models/              # User, Room, Message (Mongoose schemas)
    β”œβ”€β”€ routes/              # REST routes (/api/v1/users)
    β”œβ”€β”€ utils/               # email (Brevo), token factory, error helpers
    └── views/email/         # Pug templates for emails

Security Notes

  • JWT-authenticated sockets β€” unauthenticated connections are rejected; the message sender is taken from the token, not the client payload.
  • Helmet sets hardened HTTP headers; CORS can be locked to CLIENT_URL.
  • Rate limiting (200 req/min per IP) on /api, with trust proxy enabled so client IPs are read correctly behind Render's proxy.
  • express-mongo-sanitize strips $/. operators to prevent NoSQL injection; JSON bodies are capped at 10kb.
  • Presence is in-memory, which is correct for a single instance. To scale horizontally, add the Socket.IO Redis adapter and move presence to a shared store.

Roadmap

  • Avatar / photo upload
  • File & image messages
  • Group chats
  • Message editing & deletion
  • Push notifications

Author

Mustafa Elsharawy β€” @MeMoElprince

Contributions and issues are welcome. If you find this project useful, consider giving it a ⭐.

Releases

No releases published

Packages

 
 
 

Contributors