SkillSwap: How a LocalStorage Prototype Became a Real-Time Skill Barter Engine

A MERN refactor that keeps the original UI intact while adding persistent state, denormalized documents, JWT sessions, Socket.io signaling, and peer-to-peer video calls.

8 to 10 min read • View on GitHub • More from kanishkk-singh

Two people exchange skill cards across a table while a laptop in the center shows a clean profile list and swap queue. The scene explains how SkillSwap turns a simple barter idea into structured software without losing the feel of direct human exchange.
SkillSwap’s core move is to preserve the barter metaphor while giving it persistent state and realtime coordination.
Key Takeaways

SkillSwap is interesting because it refuses the usual refactor script. The app moves from local-only storage to a MERN backend, but it keeps the flat, display-first shape of the original UI. That choice shows up in the data model, the authentication flow, and even the realtime call setup.

The prototype that refused to die

The cleanest way to understand this repo is as a migration story. A frontend-first barter app was lifted into a full stack system, but the product still behaves like the old version: profiles stay simple, listings stay readable, and the mental model stays flat. The backend exists to make that simplicity durable.

That is a stronger design constraint than it sounds. Instead of redesigning the app around normalized database purity, the code preserves the shape the UI already wants. The result is a system that feels lightweight from the front, even though it now has persistence, auth, and realtime coordination behind it.

A close-up filing system shows a SwapRequest document containing both references and copied display fields. The visual contrast explains how SkillSwap trades database purity for faster rendering and simpler frontend reads.
SkillSwap stores display-ready fields in the backend so the frontend can render a swap card without extra lookups.

Why the backend copies the frontend

The most revealing part of the data layer is its deliberate denormalization. In models like Skill and SwapRequest, the repository stores both references and display fields such as names and usernames. That means the API can return enough information for the page to render immediately, without stitching together multiple collections at read time.

// Skill and swap documents keep display fields close to the data the UI needs
const skill = {
  title: 'Web Development',
  offeredBy: user.fullName,
  username: user.username,
  category: 'Technology'
};

const swapRequest = {
  skill: skill._id,
  requester: user._id,
  owner: skill.owner,
  skillName: skill.title,
  requestedByName: user.fullName,
  status: 'pending'
};

This flow shows how SkillSwap turns discovery into a request state machine and then into a direct browser-to-browser call.

The tradeoff is obvious: denormalized data can drift if profile fields change. But the payoff is equally obvious in a small product like this. The frontend stays fast, the queries stay simple, and the UI does not need a join-heavy rendering path to keep the experience coherent.

A swap is a state machine, not a message

SkillSwap treats barter as an evolving process. A request is not just sent and answered. It moves through states like pending, accepted, and active, which gives the system a clear way to represent negotiation and commitment.

That matters because exchange is the product here, not browsing. The app has to know whether a request is waiting, approved, or already in motion. Once you see that, the backend stops looking like a generic marketplace API and starts looking like a small workflow engine.

StageWhat the system knowsWhy it matters
PendingA request exists but no one has committed yetKeeps negotiation explicit
AcceptedBoth sides agree to proceedCreates a clear handoff point
ActiveThe exchange is underwaySupports the next realtime step
Completed or rejectedThe outcome is recordedPreserves the exchange history

This is a better model than a loose inbox of messages. It keeps the product honest about the social contract it is trying to support: reciprocal exchange, not casual chat.

Realtime, but only where it matters

The realtime layer is narrow on purpose. Socket.io is used as signaling for WebRTC, not as a media transport. The server helps two browsers find each other, exchange offer and answer data, and pass ICE candidates, but the video itself moves peer to peer.

io.on('connection', (socket) => {
  socket.on('join-room', (roomId) => socket.join(roomId));
  socket.on('offer', (payload) => socket.to(payload.roomId).emit('offer', payload));
  socket.on('answer', (payload) => socket.to(payload.roomId).emit('answer', payload));
  socket.on('ice-candidate', (payload) => socket.to(payload.roomId).emit('ice-candidate', payload));
});

That separation is the right move for a project of this shape. The server coordinates the handshake, but it does not become a video relay bottleneck. It keeps the architecture small while still giving the product a meaningful realtime step after acceptance.

The app is opinionated about discovery

Discovery lives in a single skill query path that combines search, category filtering, and sort order. That keeps the UI simple and gives the product one obvious entry point for list views, without scattering the logic across multiple endpoints.

ApproachStrengthCost
Single query pathEasy for the frontend to consumeCan grow complex inside one controller
Separate endpointsMore explicit responsibilitiesMore client orchestration
Client-side filtering onlyCheap to prototypeBreaks down as data grows

For a small marketplace, that is a good compromise. The app is still easy to reason about, but it has enough structure to handle search without turning every page into a bespoke data-fetching puzzle.

What SkillSwap is really competing with

SkillSwap is not trying to outcatalog Skillshare or outcredential Coursera. It sits in a different category entirely. The point is mutual exchange, not content consumption.

PlatformPrimary modelSkillSwap's difference
SkillsharePaid content librarySkillSwap replaces one-way learning with reciprocal exchange
LinkedIn LearningProfessional course deliverySkillSwap makes both users teachers and learners
CourseraStructured education and certificatesSkillSwap is not credential-first
P2PUPeer learning communitySkillSwap adds a direct barter workflow
Moodle or Open edXCourse managementSkillSwap is lighter and more exchange-driven

That positioning matters. The product’s value is not scale, catalog depth, or certification. It is that it models a relationship people already understand, then gives that relationship software state, persistence, and a realtime endpoint.

The tradeoffs that shape the product

The codebase is thoughtful, but it is also clearly optimized for momentum. Hardcoded allowed origins reduce portability. Denormalized user data trades integrity for speed. And the architecture reads like a single-contributor project that values clarity over enterprise ceremony.

That is not a flaw in the context of this repo. It is the point. SkillSwap shows how far a focused refactor can go when the priority is to preserve product behavior, keep the UI familiar, and add just enough backend machinery to make the idea real.