Modern marketplaces aren’t just about CRUD APIs — they’re about correctness, trust, and real-time feedback. In this post, I’ll walk through how I built GigFlow, a full-stack freelance marketplace where clients can post jobs, freelancers can bid, and hiring happens atomically and in real time.
Demo Video:
This project focuses heavily on:
- Secure authentication
- Correct hiring logic
- Preventing race conditions
- Real-time updates using Socket.io
🚀 What is GigFlow?
GigFlow is a mini freelance marketplace where:
- Any authenticated user can post a gig (client role)
- Any user can bid on gigs (freelancer role)
- A client can hire exactly one freelancer per gig
- All other bids are automatically rejected
- The hired freelancer gets a real-time notification
The core challenge wasn’t the UI — it was ensuring data integrity and correctness under concurrency.
🧱 Tech Stack
Frontend
- React (Vite)
- Tailwind CSS
- Context API for state management
- Socket.io Client
- Fetch API with credentials
Backend
- Node.js + Express
- MongoDB + Mongoose
- JWT Authentication (HttpOnly cookies)
- Socket.io
- MongoDB Transactions
Deployment
- Frontend: Vercel
- Backend: Render
- Database: MongoDB Atlas
🔐 Authentication (JWT + HttpOnly Cookies)
Authentication is handled using JWT stored in HttpOnly cookies, which:
- Prevents access from JavaScript (XSS-safe)
- Works cleanly with
credentials: "include"
Each request:
- Verifies the JWT
- Attaches the authenticated user to
req.user
This enables role-less design — users can act as both client and freelancer without separate accounts.
📦 Core Data Models
User
{
name,
email,
password
}
Gig
{
title,
description,
budget,
ownerId,
status: "open" | "assigned",
assignedTo
}
Bid
{
gigId,
freelancerId,
message,
price,
status: "pending" | "hired" | "rejected"
}
🧠 The Hiring Problem (The Hard Part)
The most important rule:
Only one freelancer can be hired for a gig — ever.
Edge case:
- Two clients (or two tabs) click Hire at the same time
- Without protection, two bids could be marked as hired
This is where many systems fail.
🔒 Solving Race Conditions with MongoDB Transactions
The hire logic is wrapped inside a MongoDB session + transaction.
High-level flow:
- Start a session
- Check if the gig is still
open - Update gig →
assigned - Mark selected bid →
hired - Mark all other bids →
rejected - Commit transaction
If any step fails, everything rolls back.
This guarantees:
- Exactly one hired freelancer
- No partial or inconsistent state
⚡ Real-Time Updates with Socket.io
Once a freelancer is hired:
- The backend emits a
hiredevent to that freelancer’s socket room - The freelancer’s dashboard updates instantly
No polling. No refresh. Instant feedback.
🖥️ Frontend Flow
Client
- Create gig
- View bids
- Click Hire
- Gig becomes assigned
Freelancer
- Submit bids
- Track bid status
- Receive real-time hire notification
State is kept minimal and predictable using Context API.
🌍 Deployment Lessons
Backend (Render)
- Environment variables must be explicitly set
- CORS must allow the deployed frontend domain
- Socket.io needs the same CORS config as Express
Frontend (Vercel)
- API base URL must point to deployed backend
- Fetch requests must include credentials
🧪 What This Project Demonstrates
- Correct API design
- Secure authentication
- Transactional integrity
- Real-time communication
- Production deployment awareness
This is not a “todo app” — it’s a system designed to behave correctly under pressure.
🔗 Links
- Live App: https://account-manager-vite.vercel.app/
- GitHub Repo: https://github.com/Sufalthakre18/gigflow-fullstack-marketplace
- Home page :
🧠 Final Thoughts
The hardest part of full-stack development isn’t writing code — it’s making sure the system behaves correctly when multiple things happen at once.
GigFlow was built with that principle in mind.
Top comments (0)