FIELD NOTES / CONSUMER MARKETPLACE

The price moved.
The phone did not notice.

BidRush is a cross-platform auction marketplace for live bidding — discovery, real-time bid updates, soft-close extensions and an integrated payment flow, with the backend holding sole authority over auction state on iOS and Android.

MARKETPLACE CASE STUDY

BidRush

Server-authoritative bidding.
Safe on a bad connection.

Built and delivered
BidRush / Live auctionACTUAL PRODUCT INTERFACE
BidRush live auction screen showing the current bid and remaining time
THE SYNCHRONISATION PROBLEMTwo devices, one last place.

Two phones can submit valid-looking bids against the same displayed price. The server has to decide which transition is real, and prevent a race in the auction record.

Follow a bid ↗
BrowseBidValidateExtendSettle
DOMAIN

Consumer marketplace / auctions

WHAT WE BUILT

Real-time mobile auction platform

OUR ROLE

Product architecture to real-time backend

01 / THE STALE STATE PROBLEM

An auction is a race
run over an unreliable network.

A marketplace built mainly on CRUD APIs and periodic polling produces visible inconsistencies: outdated prices, conflicting bids, unfair last-second outcomes, duplicate bid requests and delayed post-auction settlement.

Live auctions happen on real mobile networks. A bidder can lose connectivity after the server accepts a bid but before the app receives the response — so retries, reconnection and authoritative recovery have to be part of the design rather than an afterthought.

BidRush was built around a different principle: the backend remains the authoritative auction engine, while mobile clients receive fast real-time updates and recover safely whenever connectivity changes.

01

Bidding against a price that already changed

Active participants need the latest accepted bid and closing time without repeatedly refreshing. A bid placed against a stale price is not a bid anyone can honour.

02

Simultaneous bids need a deterministic answer

Two devices submit valid-looking bids against the same displayed price. The server must decide which state transition is valid and prevent a race condition in the auction record.

03

Winners who cannot pay

If payment readiness is only checked after an auction closes, sellers end up with a winner who cannot complete. Payment authorization had to become part of the bidding lifecycle.

02 / INSIDE THE PRODUCT

Bid on a phone.
Settle on a server.

Four views of the delivered application.
Each shows another step from browsing a listing to capturing the winner's payment.

BidRush listing browse view showing auctions available to join

Discovery before the pressure starts.

Buyers browse listings and open the auctions they want to follow, with seller media delivered through S3 and CloudFront rather than repeatedly pulled from a single origin.

BidRush listing browse view showing auctions available to join
BROWSE TO SETTLED PURCHASE

BrowseJoin roomBidValidateSoft closeWinCapture

03 / THE SERVER DECIDES, ALWAYS

The client displays state.
It never owns it.

Every bid is validated by the backend against the current auction state, official server time, the minimum next amount, bidder eligibility, payment readiness, an idempotency key and the auction version. The phone's opinion of the price is never the deciding factor.

That is also what makes reconnection safe: the client does not assume it received every event. It loads an authoritative snapshot, subscribes to what follows, and refreshes the snapshot again after any reconnection.

Look under the hood ↘
01

A bid is a command, not a broadcast

Bid submission stays an authenticated request with transactional semantics. Socket.io carries continuous event delivery, but it is not the transport a bid depends on.

BID ACCEPTED

State advances for everyone

The auction record moves to the new price and version, and the change is published to every connected participant and pushed to those who are not.

BID REJECTED

Nothing changes

A bid below the minimum, against a stale version, or from an ineligible bidder is refused outright — the auction record never enters an ambiguous state.

02

Retries cannot create duplicates

Each bid carries a stable idempotency key, so a retry returns the original outcome rather than placing a second bid — which matters most in exactly the moments the network is worst.

03

Closing time can move, by rule

A valid bid in the final 60 seconds extends the auction by 60 seconds, subject to a configured cap, so outcomes reflect intent rather than network timing.

04 / THE TECHNOLOGY & THE REASON

One durable truth.
Many fast copies.

The split runs through the whole stack: PostgreSQL holds what must be correct, Redis and Socket.io distribute what must be fast, and Stripe decides what must be paid — none of them standing in for another.

MOBILE CLIENT

React Native + Expo

A shared iOS and Android product experience, with typed application contracts and platform-appropriate secure storage.

Why one codebase?

iOS and Android can drift into separate feature implementations. A shared foundation keeps auction behaviour identical on both, which matters when the rules decide who wins.

AUCTION ENGINE

Node.js + Express

Explicit domain modules for identity, auctions, the bidding engine, listings, payments and notifications.

Why keep it one service?

The backend can hold clear domain boundaries without taking on service-management overhead that the product does not yet need.

DURABLE TRUTH

PostgreSQL

The authoritative record for auctions, bids, users and transaction state.

Why relational for bids?

Deciding which of two simultaneous bids is valid is a transactional question. It needs a store that can refuse the second one.

EVENT DISTRIBUTION

Redis Pub/Sub

Fast distribution of accepted auction changes out to connected clients.

Why separate from the database?

PostgreSQL is truth; Redis is speed. Conflating the two would either slow the auction record down or make the fast path authoritative.

REAL-TIME TRANSPORT

Socket.io

Persistent connections delivering live price, status and closing-time changes to active auction rooms.

Why not bid over the socket?

Continuous delivery and transactional commands are different guarantees. The socket keeps everyone current; the bid itself stays a request that can be retried safely.

PAYMENT LIFECYCLE

Stripe

Tokenization and authorization during bidding, capture after the auction closes, with webhooks reconciling asynchronous payment state.

Why authorize before the end?

Its role is not checkout. Establishing payment readiness during the auction is what reduces winners who cannot complete the purchase.

The confirmed implementation is React Native with Expo, Node.js/Express, PostgreSQL, Redis Pub/Sub, Socket.io, Stripe authorization and capture, Amazon S3, CloudFront, APNs and Firebase Cloud Messaging. The source also documents a deeper production-grade AWS reference architecture; those components are architectural extensions rather than confirmed deployed infrastructure, and are not claimed here.

Explore the documented platform stack

Mobile
React Native · Expo · typed contracts · secure storage
Application
Node.js · Express · auction and bidding modules
Data
PostgreSQL · authoritative auction and bid records
Real-time
Redis Pub/Sub · Socket.io · auction rooms
Payments
Stripe authorization and capture · signed webhooks
Media & push
Amazon S3 · CloudFront · APNs · Firebase Cloud Messaging

Technology names identify the documented implementation, not partnerships or endorsements.

A CONVERSATION ABOUT YOUR MARKETPLACE

What happens when two
people act at once?

Bring one flow where timing decides an outcome — bidding, booking, inventory, allocation. In 30 minutes, we will map where your state actually lives, and what a client is currently allowed to assume that it should not.

30 minutes · Your marketplace, timing and settlement

CONTACT PREVIEW

Contact destination

No message has been sent. The destination will be connected before outreach.