UniHive is a full-stack second-hand marketplace for University of Manchester students, built as a team software engineering project. It combines fixed-price listings, time-boxed auctions, bidding, saved items, profiles, reviews, reporting, JWT-protected routes, image uploads, and real-time buyer/seller chat in a React + Express + PostgreSQL application. The repo is intended to be inspectable: the database schema, seed data, REST routers, client API layer, and UI routes are all checked in, and the project achieved 2nd place in the course competition.
UniHive is technically interesting because it is not a static prototype: it models the core workflows of a marketplace end to end. The client merges fixed-price listings and auctions into one browse/search experience, auctions are backed by bid records rather than a single mutable price field, authentication bridges the University of Manchester login flow into local JWT authorization, and chat uses Socket.IO rooms alongside persisted message history. The backend exposes a broad REST API surface across 12 route modules, with PostgreSQL migrations and seed data that make the domain model auditable.
- Marketplace browsing for fixed-price listings and auctions, sorted by newest item.
- Search and pagination across listings, auctions, and filtered result views.
- Item detail pages with image carousel, seller context, auction countdowns, bid counts, save controls, and action buttons.
- Auction creation with opening bid, closing date, bid increment logic, and bid submission.
- Fixed-price listing creation with multi-image upload validation.
- Saved listings and saved auctions per authenticated user.
- Real-time messaging with Socket.IO rooms plus persisted PostgreSQL message records.
- Optional image attachments in chat and avatar upload for profiles.
- User profiles with bio/avatar editing, ratings/review count display, and super-user moderation affordances.
- Reporting, reviews, user banning/deletion, and super-user delete paths.
- JWT-based protected client routes backed by an Express auth middleware.
flowchart LR
User["Student user"] --> React["React 18 client\nReact Router + Bootstrap"]
React --> ClientAPI["client/src/api\nfetch wrappers"]
ClientAPI --> REST["Express REST API\n/api/* routers"]
ClientAPI --> Auth["/auth routes\nUoM login + JWT"]
React --> SocketClient["socket.io-client"]
SocketClient <--> SocketServer["Socket.IO server\nrooms + receive_message"]
REST --> PG["PostgreSQL\n12-table schema"]
Auth --> PG
REST --> Uploads["Image storage\nclient/public/images + server/uploads"]
REST --> Seed["db-migrate migrations\nseed data"]
- A student authenticates through
/auth/login; the backend validates the university ticket flow, creates the user if needed, signs a JWT, and redirects the browser back to the React app. - Protected React routes call
/auth/checkAuthand pass the JWT fromlocalStorage. - Browse/search pages call listing and auction endpoints, prefix item IDs as
listingid*orauctionid*, merge both item types, and sort bycreated_at. - Creating an auction inserts the auction row and an initial bid row for the opening bid; future bids are stored in
bidand read with aggregate highest-bid queries. - Chat creates or reuses a
messageRoom, persists messages through REST, and broadcasts live updates through Socket.IO room events.
The app depends on a local PostgreSQL instance for live screenshots. The checked-in repo does include the production visual assets and seeded catalogue images used by the UI:
| UniHive branding | Homepage illustration | Seed catalogue examples |
|---|---|---|
![]() |
![]() |
![]() |
Additional seeded item images are in client/public/images/, including a gaming laptop, vintage camera, oil painting, electric guitar, art supplies, sports equipment, and tech gadgets.
| Layer | Technologies |
|---|---|
| Frontend | React 18, Create React App, React Router, React Bootstrap, Bootstrap, Font Awesome |
| Backend | Node.js, Express, Socket.IO, Express Session, JSON Web Tokens |
| Database | PostgreSQL, pg, db-migrate, db-migrate-pg |
| Media | Multer uploads, MIME validation with file-type and read-chunk |
| Tooling | npm lockfiles, nodemon, database migration scripts |
.
|-- client/
| |-- public/images/ # Seed item images and public app assets
| |-- src/api/ # Frontend API wrappers for REST + Socket.IO
| |-- src/components/ # Listing cards, chat, profile, auction UI, etc.
| |-- src/routes/ # React Router pages
| `-- package.json
|-- server/
| |-- app.js # Express app, static serving, Socket.IO setup
| |-- auth/ # University auth bridge and JWT issue/check routes
| |-- middleware/ # JWT middleware
| |-- migrations/ # PostgreSQL schema and seed data
| |-- routes/ # REST routers for users, listings, auctions, bids, chat, etc.
| |-- database.json # db-migrate local database config
| `-- package.json
|-- branch_strategy.png
|-- commits_summary.txt # Generated commit-history summary
`-- README.md
The checked-in Express code defines 80 route handlers across REST and auth modules:
| Area | Route module | Handler count |
|---|---|---|
| Auctions | server/routes/auctions.js, auction_images.js, bids.js |
20 |
| Listings | server/routes/listings.js, listing_images.js |
13 |
| Messaging | server/routes/messages.js |
14 |
| Saved items | server/routes/saved_items.js |
10 |
| Users/auth | server/routes/users.js, server/auth/auth.js |
12 |
| Trust/safety | server/routes/reviews.js, reports.js |
9 |
| Uploads | server/routes/image_upload.js |
2 |
Method split: 40 GET, 23 POST, 6 PUT, and 11 DELETE handlers.
The migration in server/migrations/unihive-db/sqls/20240131141539-unihive-up.sql creates 12 tables:
app_userauctionbidlistingmessageRoommessagereviewreportlisting_imageauction_imagesaved_auctionssaved_listings
The seed script inserts super-users, sample users, 5 auctions, 5 fixed-price listings, initial bids, and associated image rows.
- Node.js and npm
- PostgreSQL
db-migrateanddb-migrate-pg
npm install -g db-migrate db-migrate-pg nodemoncd server
npm install
cd ../client
npm installCreate server/.env:
DB_USER=unihive
DB_PASSWORD=unihiveftw
DB_HOST=localhost
DB_PORT=5432
DB_NAME=unihive
JWT_SECRET=replace_with_a_local_secret
SESSION_SECRET=replace_with_a_local_secret
SERVER_HOST=localhost
CLIENT_HOST=localhostCreate client/.env from the checked-in example:
REACT_APP_SERVER_HOST=localhost
REACT_APP_CLIENT_HOST=localhostIn psql:
CREATE USER unihive WITH PASSWORD 'unihiveftw';
ALTER USER unihive SUPERUSER;
CREATE DATABASE unihive OWNER unihive;Then run the migrations and seed data:
cd server
npm run create-db
npm run insert-dataTo drop the schema:
cd server
npm run drop-dbStart the backend:
cd server
npm startStart the frontend in a second terminal:
cd client
npm startDefault URLs:
- Frontend:
http://localhost:3000 - Backend:
http://localhost:5000 - REST API root:
http://localhost:5000/api - Auth root:
http://localhost:5000/auth
The repo has no dedicated automated test suite beyond the default Create React App test harness, but there are several concrete verification points:
server/package.jsondefines database lifecycle scripts:create-db,insert-data, anddrop-db.server/migrations/unihive-db/sqls/20240131141539-unihive-up.sqldefines the complete schema.server/migrations/unihive-data/sqls/insert.sqlprovides reproducible demo data.client/src/api/items.jsmaps the React app to the backend API and covers listings, auctions, saved items, messages, bids, users, reviews, and uploads.server/routes/*.jscontains the full REST implementation.server/app.jswires Express, static serving, auth routes, API routes, upload serving, and Socket.IO messaging.
Lightweight static checks performed while preparing this README:
Source files inspected: client/src, server routes/auth/migrations/configs, package files, changelogs, assets
Non-dependency source/config files counted: 71
Non-dependency source/config lines counted: ~5.9k
Express/auth route handlers counted: 80
Database tables counted from migration: 12
Seed demo rows: 5 auctions, 5 listings, 5 opening bids, sample users, image rows
- Separate listing and auction models: Fixed-price items and auctions have different lifecycle rules, so the backend keeps separate
listingandauctiontables while the frontend merges them for browsing. - Bids as event records: Auction price is derived from bid rows (
MAX(bid.amount)) instead of mutating an auction price field. This preserves bid history and keeps opening bids consistent with later bids. - JWT at the app boundary: The app bridges the university authentication service into a local JWT, allowing protected React routes and backend middleware without re-checking the external auth service on every request.
- Socket.IO plus REST persistence: Socket.IO gives live chat delivery, while REST endpoints persist message history and room metadata in PostgreSQL.
- Local media storage: Uploads are stored in local app directories and validated as images. This is simple for a coursework/demo deployment, but a production marketplace would normally move these files to object storage.
- Manual pagination in route handlers: Current routes fetch query results and slice in application code. This is easy to understand for small seeded datasets; production-scale data would use SQL
LIMIT/OFFSETor cursor pagination. - No integrated payment flow: The FAQ explicitly positions UniHive as a marketplace connector. Payment and handoff are left to student buyers/sellers, reducing compliance scope for the project.
UniHive was delivered as a University of Manchester team project. The checked-in changelogs and commit summary show work across backend schema design, REST API delivery, React route/component implementation, authentication, auctions, saved items, messaging, profiles, image uploads, moderation, and final polish. The finished project placed 2nd in the course competition.
- The backend expects a local PostgreSQL database and the University authentication flow; without those, protected app screens are not fully usable.
- The project does not include a production deployment configuration.
- There is no committed automated backend test suite.
- The root README previously referenced
server/.env.example, but onlyclient/.env.exampleis present in this checkout; the server environment variables are documented above.


