Skip to content

Commit 5d95cb8

Browse files
committed
Fix Bitcoin price updates in production with anti-caching and direct API calls
1 parent ca2f299 commit 5d95cb8

2 files changed

Lines changed: 119 additions & 24 deletions

File tree

pages/api/coinbase-realtime.js

Lines changed: 77 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -86,41 +86,95 @@ export default async function handler(req, res) {
8686
return res.status(200).end();
8787
}
8888

89-
// Log request in production for debugging
89+
// VERY IMPORTANT: Set headers to prevent caching of this API response
90+
// This ensures we always get fresh price data
91+
res.setHeader('Cache-Control', 'no-store, no-cache, must-revalidate, proxy-revalidate');
92+
res.setHeader('Pragma', 'no-cache');
93+
res.setHeader('Expires', '0');
94+
res.setHeader('Surrogate-Control', 'no-store');
95+
96+
// Log request for debugging
9097
console.log('Coinbase realtime API called, environment:', process.env.NODE_ENV);
91-
// If WebSocket is not available or hasn't received data yet
92-
if (!lastPrice) {
93-
console.log('WebSocket data not available, fetching direct price');
98+
99+
// In production, always make a direct API call for fresh data
100+
// In development, use WebSocket if available
101+
if (process.env.NODE_ENV === 'production' || !lastPrice) {
102+
console.log('Using direct API call for price data');
94103

95-
// In production or when WebSocket fails, make a direct API call
96104
try {
97-
const response = await fetch('https://api.coinbase.com/v2/prices/BTC-USD/spot');
98-
const data = await response.json();
105+
// Try multiple price sources for reliability
106+
const coinbasePromise = fetch('https://api.coinbase.com/v2/prices/BTC-USD/spot', {
107+
headers: { 'Cache-Control': 'no-cache' }
108+
});
109+
110+
// Add a backup API in case Coinbase fails
111+
const backupPromise = fetch('https://api.coingecko.com/api/v3/simple/price?ids=bitcoin&vs_currencies=usd', {
112+
headers: { 'Cache-Control': 'no-cache' }
113+
});
114+
115+
// Race the promises to get the fastest response
116+
const responses = await Promise.allSettled([coinbasePromise, backupPromise]);
117+
118+
// Try Coinbase first
119+
if (responses[0].status === 'fulfilled') {
120+
const response = responses[0].value;
121+
const data = await response.json();
122+
123+
if (data && data.data && data.data.amount) {
124+
const price = parseFloat(data.data.amount);
125+
126+
return res.status(200).json({
127+
success: true,
128+
price: price,
129+
timestamp: Date.now(),
130+
source: 'coinbase-direct'
131+
});
132+
}
133+
}
99134

100-
if (data && data.data && data.data.amount) {
101-
const price = parseFloat(data.data.amount);
135+
// Try CoinGecko as backup
136+
if (responses[1].status === 'fulfilled') {
137+
const response = responses[1].value;
138+
const data = await response.json();
102139

140+
if (data && data.bitcoin && data.bitcoin.usd) {
141+
const price = data.bitcoin.usd;
142+
143+
return res.status(200).json({
144+
success: true,
145+
price: price,
146+
timestamp: Date.now(),
147+
source: 'coingecko-backup'
148+
});
149+
}
150+
}
151+
152+
throw new Error('All price APIs failed');
153+
} catch (error) {
154+
console.error('Error fetching direct price:', error);
155+
156+
// If direct API calls fail, try WebSocket as last resort
157+
if (lastPrice && Date.now() - lastUpdateTime < 60000) { // Only use if less than 1 minute old
103158
return res.status(200).json({
104159
success: true,
105-
price: price,
106-
timestamp: Date.now(),
107-
source: 'direct-api'
160+
price: lastPrice,
161+
timestamp: lastUpdateTime,
162+
age: Date.now() - lastUpdateTime,
163+
source: 'websocket-fallback'
108164
});
109165
}
110-
} catch (error) {
111-
console.error('Error fetching direct price:', error);
166+
167+
// If everything fails, return error
168+
return res.status(200).json({
169+
success: false,
170+
message: 'Real-time price not available',
171+
timestamp: Date.now()
172+
});
112173
}
113-
114-
// If direct API also fails, return error
115-
return res.status(200).json({
116-
success: false,
117-
message: 'Real-time price not available',
118-
timestamp: Date.now()
119-
});
120174
}
121175

122-
// Return the most recent price from WebSocket if available
123-
res.status(200).json({
176+
// In development, use WebSocket price if available
177+
return res.status(200).json({
124178
success: true,
125179
price: lastPrice,
126180
timestamp: lastUpdateTime,

vercel.json

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,48 @@
77
},
88
"headers": [
99
{
10-
"source": "/api/(.*)",
10+
"source": "/api/coinbase-realtime",
11+
"headers": [
12+
{
13+
"key": "Cache-Control",
14+
"value": "no-store, no-cache, must-revalidate, proxy-revalidate"
15+
},
16+
{
17+
"key": "Pragma",
18+
"value": "no-cache"
19+
},
20+
{
21+
"key": "Expires",
22+
"value": "0"
23+
},
24+
{
25+
"key": "Surrogate-Control",
26+
"value": "no-store"
27+
},
28+
{
29+
"key": "X-Content-Type-Options",
30+
"value": "nosniff"
31+
},
32+
{
33+
"key": "X-Frame-Options",
34+
"value": "DENY"
35+
},
36+
{
37+
"key": "Access-Control-Allow-Origin",
38+
"value": "*"
39+
},
40+
{
41+
"key": "Access-Control-Allow-Methods",
42+
"value": "GET, OPTIONS"
43+
},
44+
{
45+
"key": "Access-Control-Allow-Headers",
46+
"value": "X-CSRF-Token, X-Requested-With, Accept, Accept-Version, Content-Length, Content-MD5, Content-Type, Date, X-Api-Version, x-cg-api-key"
47+
}
48+
]
49+
},
50+
{
51+
"source": "/api/((?!coinbase-realtime).*)",
1152
"headers": [
1253
{
1354
"key": "Cache-Control",

0 commit comments

Comments
 (0)