realtime-auction-nextjs-supabase.html
< BACK "Building a Real-Time Auction Site with Next.js & Supabase" के लिए हीरो इमेज

Next.js और Supabase के साथ रीयल-टाइम नीलाम साइट बनाना

एक क्लाइंट ने मुझे 2021 में गुरुवार की दोपहर को फोन किया -- बाथ में एक एंटीक डीलर, अपनी मासिक आमने-सामने नीलामी को ऑनलाइन ले जाना चाहता था। काफी सरल है, मैंने सोचा। फिर उसने कहा "और बोलियों को सभी के लिए अपडेट होना चाहिए, लाइव, कोई पेज रीफ्रेश नहीं।" ठीक है। उसी समय एक "सरल WordPress काम" दो हफ्ते की आर्किटेक्चर बातचीत में बदल गया।WordPress job" turned into a two-week architecture conversation.

मुख्य बात: Next.js और Supabase पर लाइव बिडिंग रीयलटाइम चैनलों, रो-लेवल सिक्योरिटी और सर्वर-साइड बिड वेलिडेशन पर निर्भर करती है; UI से पहले स्टेट मशीन को सही तरीके से सेट करें।Live bidding on Next.js plus Supabase hinges on realtime channels, row-level security, and server-side bid validation; get the state machine right before the UI.

मैंने Seahawk Media में 12,000 से अधिक साइटें बनाई हैं, और रीयल-टाइम फीचर वो हैं जो आपको काटते हैं अगर आप शुरुआत से ही उन्हें ठीक से प्लान नहीं करते। हर पाँच सेकंड पोल करना ठीक लगता है जब तक आपके पास 200 बोलीदाता एक ही एंडपॉइंट पर एक साथ हथौड़ा नहीं मार रहे होते और आपका होस्टिंग बिल रातोंरात दोगुना नहीं हो जाता। तो मैं आपको बिल्कुल दिखाता हूँ कि मैं एक सही लाइव नीलाम प्लेटफॉर्म आज कैसे बनाऊंगा -- Next.js और Supabase का उपयोग करके -- जो मैंने असल में शिप किया है।Seahawk Media, and real-time features are the ones that bite you if you don't plan them properly from the start. Polling every five seconds sounds fine until you have 200 bidders hammering a single endpoint simultaneously and your hosting bill doubles overnight. So let me show you exactly how I'd build a proper live auction platform today -- using Next.js and Supabase -- based on what I've actually shipped.

---

इसके लिए विशेष रूप से Next.js और Supabase क्यों

देखिए, रीयल-टाइम करने के दर्जन तरीके हैं। Node सर्वर पर Socket.io, Ably, Pusher, Firebase -- मैंने सभी का उपयोग विभिन्न बिंदुओं पर किया है। लेकिन Next.js + Supabase कॉम्बिनेशन यहाँ अपनी जगह अर्जित करता है एक विशेष कारण के लिए: Supabase Realtime PostgreSQL के लॉजिकल रेप्लिकेशन के ऊपर बनाया गया है, जिसका मतलब है कि आपकी लाइव बिड अपडेट और आपकी परसिस्टेंट डेटा लेयर एक ही सिस्टम हैं। कोई दो ट्रुथ के सोर्स को सिंक करना नहीं। कोई यह सोचना नहीं कि क्या एक बिड जो WebSocket में गई भी डेटाबेस में बनी रही।same system. No syncing two sources of truth. No wondering if a bid that went into the WebSocket also made it into the database.

Supabase आपको Auth, Row Level Security, और Storage सभी बॉक्स में मिलते हैं। एक नीलामी साइट के लिए, जहां "केवल नीलामी मालिक ही लॉट समाप्त कर सकता है" और "एक यूजर अपनी खुद की वस्तु पर बोली नहीं लगा सकता" असली बिज़नेस नियम हैं, Postgres में RLS policies बिल्कुल सही टूल हैं।

और Next.js क्योंकि -- ईमानदारी से -- App Router Server Components के साथ मतलब है कि आप नीलाम कैटलॉग को स्टेटिकली रेंडर कर सकते हैं, SEO को खुश रख सकते हैं, और केवल क्लाइंट पर रीयल-टाइम बिडिंग विजेट को हाइड्रेट करते हैं। वह स्प्लिट मायने रखता है। आप एक पेज पर डायनामिक रेंडरिंग के लिए भुगतान नहीं करना चाहते जो 90% स्टेटिक कंटेंट है।

---

पहले स्कीमा डिज़ाइन करना (इसे स्किप न करें)

यह वह जगह है जहां ज्यादातर लोग जल्दबाजी करते हैं और बाद में इसका पछतावा करते हैं। मैंने Bath एंटीक क्लाइंट के स्कीमा को प्रोजेक्ट के बीच रीफैक्टर करने में शर्मनाक तीन दिन बिताए क्योंकि मैंने bid history मॉडल के बारे में सही तरीके से नहीं सोचा था।

यहाँ मूल संरचना है जो मैं अब उपयोग करता हूं:

  • `profiles` -- Supabase के auth.users को एक्सटेंड करता है, डिस्प्ले नाम, वेरिफाइड बोलीदाता फ्लैग, और एक credit_balance स्टोर करता है अगर आप डिपॉजिट-बेस्ड बिडिंग कर रहे हैं -- extends Supabase's auth.users, stores display name, verified bidder flag, and a credit_balance if you're doing deposit-based bidding
  • `auctions` -- इवेंट खुद; starts_at, ends_at, status (draft | live | closed), और created_by -- the event itself;starts_at,ends_at,status(draft | live | closed), and created_by
  • `lots` -- नीलाम के भीतर व्यक्तिगत आइटम; reserve_price, current_bid, current_bidder_id, lot_number, ends_at (लॉट के व्यक्तिगत काउंटडाउन हो सकते हैं) -- individual items within an auction;reserve_price,current_bid,current_bidder_id,lot_number,ends_at(lots can have individual countdowns)
  • `bids` -- immutable append-only लॉग; lot_id, bidder_id, amount, placed_at। कभी इस टेबल को अपडेट न करें। कभी नहीं। -- immutable append-only log;lot_id,bidder_id,amount,placed_at. Never update this table. Ever.
  • `auction_participants` -- एक जॉइन टेबल जो ट्रैक करता है कि किसने कौन सी नीलामी के लिए रजिस्टर किया (डिपॉजिट होल्ड और नोटिफिकेशन टार्गेटिंग के लिए उपयोगी) -- a join table tracking who has registered for which auction (useful for deposit holds and notification targeting)

lots पर current_bid और current_bidder_id columns को intentionally denormalised किया गया है। हाँ, आप उन्हें हर read पर bids table से derive कर सकते हैं, लेकिन concurrent load के तहत वो query expensive हो जाती है जल्दी। इसे denormalise करें, bids table को अपने audit log के रूप में रखें, और एक Postgres function का उपयोग करें lots को atomically update करने के लिए जब एक bid accept किया जाए।current_bid and current_bidder_id columns on lots are denormalised intentionally. Yes, you could derive them from the bids table on every read, but under concurrent load that query gets expensive fast. Denormalise it, keep the bids table as your audit log, and use a Postgres function to update lots atomically when a bid is accepted.

The Atomic Bid Function

यह वह हिस्सा है जो ज़्यादातर ट्यूटोरियल छोड़ देते हैं। नीलामी में रेस कंडीशन असली होती हैं। दो यूजर्स एक ही मिलीसेकंड में £520 सबमिट करें -- क्या होता है?real. Two users submitting £520 at the same millisecond -- what happens?

जवाब है एक Postgres function जिसमें lot row पर FOR UPDATE locking है:FOR UPDATE locking on the lot row:

``` create or replace function place_bid(p_lot_id uuid, p_bidder_id uuid, p_amount numeric) returns json as $$ declare v_lot lots%rowtype; begin select * into v_lot from lots where id = p_lot_id for update;

if v_lot.status!= 'live' then return json_build_object('success', false, 'error', 'Lot is not live'); end if;

if p_amount <= v_lot.current_bid then return json_build_object('success', false, 'error', 'Bid too low'); end if;

if p_bidder_id = v_lot.current_bidder_id then return json_build_object('success', false, 'error', 'You are already the highest bidder'); end if;

insert into bids (lot_id, bidder_id, amount) values (p_lot_id, p_bidder_id, p_amount);

update lots set current_bid = p_amount, current_bidder_id = p_bidder_id where id = p_lot_id;

return json_build_object('success', true, 'new_bid', p_amount); end; $$ language plpgsql security definer; ```

इसे अपने Next.js API route से call करें supabase.rpc('place_bid', {...}) के द्वारा। FOR UPDATE lock का मतलब है केवल एक transaction हर given moment में प्रति lot जीतता है। दूसरा एक serialisation error को पाता है और आप client पर एक friendly "someone just outbid you" message return करते हैं।supabase.rpc('place_bid', {...}). The FOR UPDATE lock means only one transaction wins per lot at any given moment. The other one gets a serialisation error and you return a friendly "someone just outbid you" message on the client.

---

Row Level Security -- नीलामी के नियमों की परत

RLS उन चीजों में से एक है जो developers या तो तुरंत पसंद करते हैं या टालते हैं क्योंकि यह अपारदर्शी लगता है। मैं टालने वाले पक्ष में था जब तक कि Seahawk पर एक fintech प्रोजेक्ट ने मुझे सिखाया कि केवल application code में access control को enforce करना एक गलत तरीके से configured API route दूर है।

एक नीलामी साइट के लिए, ये नीतियां मायने रखती हैं:

  1. कोई भी लाइव लॉट्स को पढ़ सकता है -- SELECT on lots where auctions.status = 'live' -- SELECT on lots where auctions.status = 'live'
  2. केवल सत्यापित, वेरीफाई किए गए बोली लगाने वाले ही बिड डाल सकते हैं -- पॉलिसी में profiles.verified_bidder = true चेक करें -- check profiles.verified_bidder = true in the policy
  3. केवल नीलामी का निर्माता ही लॉट की स्थिति अपडेट कर सकता है -- UPDATE on lots where auctions.created_by = auth.uid() -- UPDATE on lots where auctions.created_by = auth.uid()
  4. बिड हिस्ट्री लॉट की नीलामी के निर्माता और बोली लगाने वाले को पढ़ने की अनुमति है -- कोई और को रीयल टाइम में पूरी बिड हिस्ट्री देखने की ज़रूरत नहीं है -- no one else needs to see full bid history in real time

Supabase की RLS डॉक्यूमेंटेशन यहाँ सच में अच्छी है -- security definer functions पर सेक्शन पढ़ने लायक है, क्योंकि यह इंटरेक्ट करता है कि place_bid जैसी RPC कॉल्स कैसे काम करती हैं।Supabase RLS documentation is genuinely good here -- worth reading the section on security definer functions, because it interacts with how RPC calls like place_bid work.

एक अड़चन: अगर आप अपने Postgres फंक्शन पर security definer का इस्तेमाल करते हैं (जैसे ऊपर), तो यह फंक्शन ओनर के प्रिविलेजेस के साथ चलता है, RLS को बायपास करते हुए। यह इरादे से है -- आप चाहते हैं कि बिड प्लेसमेंट बिडर की RLS को बायपास करे ताकि यह लॉट रो को लॉक और अपडेट कर सके। लेकिन इसका मतलब है कि आपको फंक्शन के अंदर अपना खुद का बिजनेस लॉजिक चेक लागू करना चाहिए, जो ऊपर दिया गया कोड करता है।security definer on your Postgres function (as above), it runs with the function owner's privileges, bypassing RLS. That's intentional -- you want the bid placement to bypass the bidder's RLS so it can lock and update the lot row. But it means you must enforce your own business logic checks inside the function, which the code above does.

---

Supabase Realtime को Next.js में सेटअप करना

यह वह जगह है जहाँ यह वास्तव में संतोषजनक हो जाता है। Supabase Realtime आपको WebSockets का उपयोग करके Postgres टेबल पर परिवर्तनों की सदस्यता लेने देता है, और क्लाइंट SDK इसे लगभग शर्मनाक रूप से सरल बना देता है।WebSockets underneath, and the client SDK makes it almost embarrassingly simple.

अपने नीलामी लॉट पेज पर -- Next.js App Router में एक Client Component -- आप कुछ इस तरह करेंगे:

``` 'use client'

import { useEffect, useState } from 'react' import { createClientComponentClient } from '@supabase/auth-helpers-nextjs'

export default function LotBidDisplay({ lotId, initialBid }) { const [currentBid, setCurrentBid] = useState(initialBid) const supabase = createClientComponentClient()

useEffect(() => { const channel = supabase.channel(lot-${lotId}).on( 'postgres_changes', { event: 'UPDATE', schema: 'public', table: 'lots', filter:id=eq.${lotId}}, (payload) => { setCurrentBid(payload.new.current_bid) } ).subscribe()lot-${lotId}).on( 'postgres_changes', { event: 'UPDATE', schema: 'public', table: 'lots', filter:id=eq.${lotId}}, (payload) => { setCurrentBid(payload.new.current_bid) } ).subscribe()

return () => { supabase.removeChannel(channel) }

return <div>Current bid: £{currentBid.toLocaleString()}</div> } ```

एक Server Component से initialBid पास करें जो रिक्वेस्ट टाइम पर ताज़ा डेटा फेच करता है। फिर क्लाइंट ले लेता है, उस विशेष लॉट रो पर UPDATE इवेंट्स को सुनते हुए। हर बार जब place_bid सफलतापूर्वक चलता है, Supabase बदलाव को ब्रॉडकास्ट करता है और हर जुड़े हुए बिडर का UI आम तौर पर 100-300ms के अंदर अपडेट हो जाता है।initialBid from a Server Component that fetches fresh data at request time. The client then takes over, listening for UPDATE events on that specific lot row. Every time place_bid runs successfully, Supabase broadcasts the change and every connected bidder's UI updates within about 100-300ms typically.

Countdown Timer को Handle करना

नीलामों में आमतौर पर एक उलटी गिनती होती है -- "3:42 में बंद होगा"। इसके लिए क्लाइंट क्लॉक पर भरोसा न करें। lots.ends_at (Postgres में UTC में स्टोर किया गया) से अंतिम समय निकालें और Date.now() का उपयोग करके क्लाइंट पर शेष सेकंड की गणना करें। ड्रिफ्ट के मामले में हर 60 सेकंड में एक ताज़ा फ़ेच से इसे फिर से सिंक करें। और "soft close" लॉजिक जोड़ें: यदि कोई बोली आखिरी 60 सेकंड के भीतर आती है, तो ends_at को दो मिनट से बढ़ाएं। यह मानक नीलामी व्यवहार है और बोली लगाने वाले इसकी अपेक्षा करते हैं।not trust the client clock for this. Derive the end time from lots.ends_at(stored in UTC in Postgres) and calculate remaining seconds on the client using Date.now(). Re-sync it every 60 seconds with a fresh fetch in case of drift. And add "soft close" logic: if a bid arrives within the last 60 seconds, extend ends_at by two minutes. That's standard auction behaviour and bidders expect it.

---

Auction UI के लिए Next.js App Router Architecture

जो page structure मैं use करूँगा:

``app/ auctions/ page.tsx ← Server Component, live auctions list करता है (ISR, revalidate: 60) [auctionId]/ page.tsx ← Server Component, server-side से lots list fetch करता है LotGrid.tsx ← Client Component, lot status changes पर subscribe करता है [lotId]/ page.tsx ← Server Component, initial lot data + SEO के लिए metadata BidPanel.tsx ← Client Component, real-time bid display + bid form``app/ auctions/ page.tsx ← Server Component, lists live auctions (ISR, revalidate: 60) [auctionId]/ page.tsx ← Server Component, fetches lots list server-side LotGrid.tsx ← Client Component, subscribes to lot status changes [lotId]/ page.tsx ← Server Component, initial lot data + metadata for SEO BidPanel.tsx ← Client Component, real-time bid display + bid form``

कैटलॉग (/auctions) 60-सेकंड के रीवेलिडेशन के साथ Incremental Static Regeneration का उपयोग करता है। अलग-अलग lot पेजेस पहली लोड पर सर्वर-साइड रेंडर होते हैं (शेयरिंग, प्रीव्यूइंग, og:image जेनरेशन के लिए), फिर लाइव stuff के लिए क्लाइंट कंपोनेंट्स को हैंड ऑफ करते हैं।/auctions) uses Incremental Static Regeneration with a 60-second revalidation. Individual lot pages render server-side on first load (for sharing, previewing, og:image generation), then hand off to client components for the live stuff.

एक चीज़ जो मैं हमेशा करता हूं: BidPanel कंपोनेंट को dynamic(() => import('./BidPanel'), { ssr: false }) के पीछे lazy-loaded रखें। यह वैसे भी केवल क्लाइंट-साइड पर समझ में आता है, और यह धीमे कनेक्शन पर उपयोगकर्ताओं के लिए आपकी प्रारंभिक HTML पेलोड को पतला रखता है -- जो, यदि आपकी नीलामी दर्शक पुराने हैं (जैसा कि प्राचीन नीलामों में होता है), तो आप जितना सोचते हैं उससे ज़्यादा महत्वपूर्ण है।BidPanel component lazy-loaded behind dynamic(() => import('./BidPanel'), { ssr: false }). It only makes sense client-side anyway, and it keeps your initial HTML payload lean for users on slow connections -- which, if your auction audience skews older (as antique auctions tend to), matters more than you'd expect.

---

Authentication और "Verified Bidder" Flow

Standard Supabase Auth जिसमें email/password या magic link साइन-अप के लिए ठीक काम करता है। लेकिन auctions को अक्सर एक extra step की जरूरत होती है: bidder verification। आपको क्रेडिट कार्ड hold, ID verification, या बस admin approval की जरूरत हो सकती है कोई कोई भी बोली लगाने से पहले।

जिस पैटर्न का मैं उपयोग करता हूँ: profiles टेबल पर verified_bidder बूलियन, डिफ़ॉल्ट रूप से false। साइन-अप के बाद, उपयोगकर्ता "अपना रजिस्ट्रेशन पूरा करें" स्क्रीन देखता है। एक बार अनुमोदन के बाद (व्यवस्थापक द्वारा मैनुअल रूप से, या Stripe पेमेंट प्राधिकरण के बाद स्वचालित रूप से), आप फ़्लैग को फ्लिप करते हैं। bids पर RLS नीति इसे जांचती है। वे ब्राउज़ कर सकते हैं, देख सकते हैं, लेकिन सत्यापित होने तक बिड नहीं कर सकते।verified_bidder boolean on the profiles table, defaulting to false. After sign-up, the user sees a "Complete your registration" screen. Once approved (manually by admin, or automatically after a Stripe payment authorisation), you flip the flag. The RLS policy on bids checks it. They can browse, watch, but not bid until verified.

Stripe भुगतान प्राधिकरण होल्ड के लिए, capture_method: manual के साथ Stripe के payment intents सही दृष्टिकोण है -- आप £50 होल्ड को प्राधिकृत करते हैं, यदि वे जीतते हैं तो इसे कैप्चर करें, यदि वे नहीं जीतते तो इसे जारी करें। यह no-pay स्थितियों को नाटकीय रूप से कम करता है जो, मुझ पर विश्वास करें, हर ऑनलाइन नीलामी ऑपरेटर के अस्तित्व का संकट है।Stripe's payment intents with capture_method: manual is the right approach -- you authorise a £50 hold, capture it if they win, release it if they don't. This dramatically reduces no-pay situations which, trust me, are the bane of every online auction operator's existence.

---

Deployment, Performance और जो Bits आपको काटेंगे

Vercel पर डिप्लॉय करें -- Next.js के लिए यह स्पष्ट विकल्प है और edge नेटवर्क Supabase के वैश्विक इंफ्रास्ट्रक्चर के साथ अच्छी तरह काम करता है। सुनिश्चित करें कि आपकी Supabase प्रोजेक्ट आपके Vercel डिप्लॉयमेंट क्षेत्र के निकटतम AWS क्षेत्र में है। मैंने 40-60ms की पूरी तरह अनावश्यक विलंबता देखी है क्योंकि किसी ने Vercel को us-east-1 में और Supabase को eu-west-2 में डिप्लॉय किया था। एक क्षेत्र चुनें, दोनों को वहां रखें।Vercel -- it's the obvious choice for Next.js and the edge network plays well with Supabase's global infrastructure. Make sure your Supabase project is in the AWS region closest to your Vercel deployment region. I've seen 40-60ms of completely unnecessary latency because someone deployed Vercel in us-east-1 and Supabase in eu-west-2. Pick one region, put both there.

कुछ चीजें हैं जो आपको दर्द देंगी अगर आप उन्हें शुरुआत से ही संभाल न लें:

  • WebSocket connection limits। Supabase की free tier लगभग 200 concurrent Realtime connections allow करती है। अगर तुम्हारा auction viral हो जाता है, तो यह cap matter करता है। अपनी plan check करो।Supabase's free tier allows around 200 concurrent Realtime connections. If your auction goes viral, that cap matters. Check your plan.
  • बोलियों के लिए Optimistic UI। बोली लगाने वाले की स्क्रीन पर सर्वर की पुष्टि से पहले बोली को तुरंत दिखाएं। यदि यह विफल हो जाए (outbid, race condition), एक त्रुटि के साथ वापस लें। 200-300ms सर्वर राउंड-ट्रिप अगोचर है जब तक UI इसका इंतज़ार नहीं करता।Show the bid immediately on the bidder's screen before the server confirms. If it fails (outbid, race condition), revert with an error. The 200-300ms server round-trip is imperceptible unless the UI waits for it.
  • नीलाम समाप्ति की अनुमति अवधि। कभी भी एक नीलाम को ends_at पर बिल्कुल बंद न करें। समय सीमा से पहले जमा की गई in-flight बोलियों को संसाधित करने की अनुमति देने के लिए सर्वर-साइड पर 2-3 सेकंड की बफर दें। इसे अपने close_lot शेड्यूल्ड फ़ंक्शन में संभालें।Never close a lot exactly at ends_at. Give it a 2-3 second server-side buffer to allow in-flight bids that were submitted just before the deadline to process. Handle this in your close_lot scheduled function.
  • ईमेल सूचनाएं। Supabase Edge Functions को Resend या Postmark के साथ "आप पर बोली लगाई गई है" और "आप जीत गए!" ईमेल भेजने के लिए उपयोग करें। अपने Next.js API routes से ऐसा करने की कोशिश न करें -- वे समय समाप्त हो सकते हैं, और नीलामी प्रतिभागियों को सूचनाएं अविश्वसनीय होने पर असली निराशा होती है।Use Supabase Edge Functions with Resend or Postmark to send "You've been outbid" and "You won!" emails. Don't try to do this from your Next.js API routes -- they can time out, and auction participants get genuinely annoyed if notifications are unreliable.

---

FAQ

Supabase Realtime कितने concurrent bidders को handle कर सकता है?

Supabase की Pro योजना डिफ़ॉल्ट रूप से 500 concurrent Realtime कनेक्शन तक सपोर्ट करती है, अधिक सीमाएं उपलब्ध हैं। अधिकांश नीलामी साइटों के लिए -- जब तक आप Sotheby's ऑनलाइन के आकार का कुछ नहीं चला रहे हैं -- यह पर्याप्त से अधिक है। यदि आप हज़ारों simultaneous दर्शकों की अपेक्षा करते हैं, तो per-user सदस्यता के बजाय एक single server-side चैनल के माध्यम से नीलाम अपडेट प्रसारित करने पर विचार करें, और Supabase की Realtime Broadcast सुविधा को देखें जो high fan-out परिस्थितियों के लिए अधिक efficient है।Realtime Broadcast feature which is more efficient for high fan-out scenarios.

क्या मुझे Supabase Realtime का इस्तेमाल करना चाहिए या Ably जैसी dedicated service का?

अधिकांश परियोजनाओं के लिए, Supabase Realtime पूरी तरह से पर्याप्त है और एकीकरण बहुत सरल है क्योंकि आपका डेटा पहले से ही Supabase में है। मैं केवल तब Ably या Pusher की ओर रुख करूंगा जब आपको globally sub-50ms latency की आवश्यकता हो, या यदि आप millions concurrent कनेक्शन के साथ कुछ बना रहे हों। एक प्राचीन वस्तुओं की नीलामी, एक charity fundraiser, एक छोटी art gallery की ऑनलाइन बिक्री -- Supabase सभी को ठीक से संभालता है।

अगर यूजर का WebSocket कनेक्शन नीलाम के बीच में ड्रॉप हो जाए तो क्या होता है?

Supabase का client SDK स्वचालित रूप से पुनः कनेक्ट करने का प्रयास करेगा। लेकिन आपको हमेशा पुनः कनेक्शन पर वर्तमान लॉट state (current_bid, ends_at) को फिर से fetch करना चाहिए बजाय disconnection से पहले local state में जो कुछ था उस पर भरोसा करने के। अपने Client Component में एक online/offline event listener जोड़ें और जब कनेक्शन बहाल हो तो एक fresh server fetch को ट्रिगर करें।current_bid,ends_at) on reconnection rather than trusting whatever was in local state before the drop. Add an online/offline event listener in your Client Component and trigger a fresh server fetch when the connection restores.

क्या मैं एक API रूट की जगह Next.js Server Actions का उपयोग करके बिड प्लेस कर सकता हूं?

हाँ, और मैंने यह किया है। Next.js 14 में Server Actions सुविधाजनक हैं -- वे एक dedicated /api/bid route की boilerplate को हटाते हैं। ट्रेडऑफ यह है कि Server Actions को individually rate-limit करना थोड़ा कठिन है (आप middleware स्तर पर rate limiting लागू करेंगे बजाय per-action)। एक production नीलामी साइट के लिए, मैं middleware में Upstash Redis rate limiting जोड़ूंगा ताकि एक single उपयोगकर्ता बोली requests को spamming करने से रोका जा सके चाहे आप Actions या API routes का उपयोग करें या नहीं।/api/bid route. The tradeoff is that Server Actions are a bit harder to rate-limit individually (you'd apply rate limiting at the middleware level rather than per-action). For a production auction site, I'd add Upstash Redis rate limiting in middleware to prevent a single user from spamming bid requests regardless of whether you use Actions or API routes.

मैं समान बोलियों को एक साथ कैसे संभालूँ -- जब दो बिल्कुल एक जैसी बोलियाँ एक ही समय पर आएँ?

place_bid Postgres function में FOR UPDATE lock concurrent bids को serialize करता है, इसलिए technically database level पर ties नहीं हो सकते। एक सफल होगा, दूसरा "bid too low" response के साथ विफल होगा (चूंकि दोनों current_bid के बराबर हैं और check p_amount <= v_lot.current_bid है)। पहला आओ, पहले सेवा पाओ। यह standard auction practice है और अधिकांश bidders इसे समझते हैं।FOR UPDATE lock in the place_bid Postgres function serialises concurrent bids, so technically ties can't happen at the database level. One will succeed, the other will fail with a "bid too low" response (since both are equal to current_bid and the check is p_amount <= v_lot.current_bid). First in, first served. That's standard auction practice and most bidders understand it.

---

बाथ के एंटीक डीलर ने, जो कुछ भी हो, दो साल से ज़्यादा समय से अपनी मासिक नीलामियाँ ऑनलाइन चला रहे हैं। एक शनिवार की शाम को एक साथ सबसे ज़्यादा 84 बोलीदाता आए -- उनका पूरा गाँव ही जॉर्जियन चाँदी की एक विवादास्पद लॉट को उसकी रिज़र्व कीमत से तीन गुना ज़्यादा में बिकते देखने आ गया। Supabase बिना झिझके चला। Next.js बिना झिझके चला। जो एकमात्र चीज़ टूटी वह था उनका वाई-फाई, क्योंकि वह इसे दुकान के फ़र्श से चला रहे थे।

रियल-टाइम को शुरुआत में सोच-समझकर लागू करना मुश्किल है, लेकिन एक बार जब स्कीमा ठीक हो जाए और परमाणु बोली फ़ंक्शन जगह पर आ जाए, तो बाकी सब कुछ ज़्यादातर पाइपलाइनिंग है। नींव को सही रखो और तुम अपना समय मज़ेदार हिस्सों पर लगाओगे -- काउंटडाउन एनिमेशन, "एक बार, दो बार" यूएक्स -- रात को दो बजे रेस कंडीशन्स को डीबग करने की जगह।

< BACK