This is a production-grade, real-time auction platform built with NestJS. It is designed to solve complex backend challenges including concurrency (race conditions), real-time updates, performance caching, and reliable background jobs.
This project serves as a demonstration of a senior-level, microservice-style architecture, proving a deep understanding of modern backend engineering principles.
- Real-time Bidding: Instant bid updates pushed to all clients via WebSockets (Socket.IO).
- Concurrency Safe: Bulletproof bidding system using PostgreSQL pessimistic locking to prevent race conditions.
- High Performance: Sub-100ms response times for "hot" auctions using a Redis cache-aside strategy.
- Reliable Auction Closure: Guaranteed, on-time auction closures using a BullMQ background job queue.
- Secure: JWT-based authentication (Passport.js) and authorization (custom guards).
- Observable: Includes
/healthchecks for monitoring. - Documented: Fully interactive API documentation via Swagger (
/api/docs).
This application follows a modern, decoupled architecture.
+------------------+
| Client |
| (Browser/App) |
+--------+---------+
| |
(HTTP) | | (WebSocket)
| |
+--v-----v---------+
| |
| NestJS Server |
| (API / Gateway) |
| |
+--+------+--------+
| |
| |
+--------v-+ +-v--------+ +--------------+
| | | | | |
| PostgreSQL | | Redis | | BullMQ |
| (Database) | | (Cache) | | (Job Queue) |
| - Users | | - Auction| | - close-job |
| - Auctions| | Cache | | - ... |
| - Bids | | | | |
+----------+ +----------+ +--------------+
Request Flow (New Bid):
- Client sends
POST /bidsto the NestJS API. BidsServicebegins a PostgreSQL transaction.- It acquires a pessimistic lock on the auction row to block other bids.
- It validates the bid and saves the new
Bidand updates theAuction. - It invalidates the Redis cache for that auction.
- The transaction is committed.
BidsServicecalls theAuctionsGatewayto broadcast the new bid.AuctionsGatewayemits thenew-bidevent to all clients in that auction's "room."
- Backend: NestJS, TypeScript
- Database: PostgreSQL (with TypeORM)
- Caching: Redis (with
cache-manager) - Queues: BullMQ (backed by Redis)
- Real-time: WebSockets (Socket.IO)
- Auth: Passport.js, JWT, bcrypt
- Validation:
class-validator - API Docs: Swagger
- Testing: Jest, Supertest
- DevOps: Docker, Docker Compose
This project was built to solve specific, high-value engineering problems:
- Problem: Two users bidding on the same item at the same millisecond can lead to a race condition, where the database state becomes inconsistent.
- Solution: The bidding logic is wrapped in a
DataSource.transaction(). Inside this transaction, we use a pessimistic write lock (.setLock('pessimistic_write')) on the specific auction row. This forces concurrent bid requests to serialize, guaranteeing data integrity at scale.
- Problem: A user shouldn't have to refresh their browser to see a new bid.
- Solution: We use a WebSocket Gateway (
AuctionsGateway). Clients join a specific "room" for each auction (e.g.,auction:<id>). When a bid is successfully processed, theBidsServicecalls the gateway toemit('new-bid', ...)to only the clients in that room.
- Problem: A popular auction's
GET /auctions/:idendpoint will be "hot," causing massive database load. - Solution: We use a cache-aside strategy. The
findOnecontroller is decorated with@UseInterceptors(CacheInterceptor), which automatically caches the result in Redis. - Cache Invalidation: To prevent stale data, the
BidsServiceexplicitly deletes the cache key (cacheManager.del(...)) inside the database transaction, guaranteeing that the cache is invalidated if and only if the new bid is successful.
- Problem: Auctions must close reliably at their
endTime, even if the server restarts. A simple cron job that scans the DB is fragile and inefficient. - Solution: We use a Producer/Consumer pattern. When an auction is created, the
AuctionsService(Producer) adds a delayed job to a BullMQ queue. A separateAuctionProcessor(Consumer) listens for these jobs and processes them at the scheduled time, ensuring reliable closure.
-
Clone the repository:
git clone https://github.com/psyberpath/nestjs-auction-system.git cd auction-service -
Create your environment file: Copy the
example.envto.envand fill in the values (the defaults will work with thedocker-compose.ymlfile).cp example.env .env
-
Start the infrastructure (Database & Cache):
docker-compose up -d
-
Install dependencies:
npm install
-
Run database migrations: This will create all the tables in your PostgreSQL container.
npm run migration:run
-
Run the application:
npm run start:dev
Your server is now running on http://localhost:3000.
This project includes a comprehensive end-to-end test suite that simulates a full, multi-user flow.
- Make sure your Docker containers are running (
docker-compose up -d). - Run the E2E test suite:
npm run test:e2e
Once the server is running, the full, interactive API documentation (powered by Swagger) is available at:
http://localhost:3000/api/docs