ایزی ریت
ورود / ثبت‌نامورود

قیمت طلا در React و Next.js؛ پیاده‌سازی تابلو قیمت لحظه‌ای طلا و سکه

نمایش قیمت طلا در React و Next.js با API بازار: Route Handler برای مخفی ماندن کلید، کش ۶۰ ثانیه‌ای، کامپوننت تابلو، به‌روزرسانی خودکار و فرمت اعداد فارسی.

عرفان فضلی
· کارشناس توسعه و برنامه نویسی
انتشار ۱ آبان ۱۴۰۵، ۱۲:۰۰ · ۵ دقیقه مطالعه
قیمت طلا در React و Next.js
خلاصه در سه خط
  • کلید API را در مرورگر قرار ندهید؛ در Next.js یک Route Handler بسازید که قیمت‌ها را از API بگیرد و کش کند.
  • کامپوننت React هر ۶۰ ثانیه از مسیر داخلی سایت شما می‌خواند و قیمت طلای ۱۸، مثقال و سکه‌ها را نشان می‌دهد.
  • اعداد را با Intl.NumberFormat و locale فارسی نمایش دهید و تغییر را با رنگ و جهت فلش نشان دهید.

یک تابلو قیمت طلا و سکه، چه برای سایت طلافروشی باشد چه برای داشبورد داخلی، سه نیاز اصلی دارد: عددها درست و تازه باشند، صفحه سریع بارگذاری شود و کلید API لو نرود. در این آموزش نمایش قیمت طلا در React را با Next.js (App Router) پیاده می‌کنیم؛ همین الگو با کمی تغییر در Vite و Create React App هم کار می‌کند.

★
پاسخ کوتاه
یک Route Handler در app/api/gold/route.ts بسازید که با کلید سمت سرور از https://api.ezrate.me/v1/rates?symbols=g18,mesghal,ounce,emami,bahar بخواند و revalidate: 60 داشته باشد. کامپوننت کلاینت هر ۶۰ ثانیه /api/gold را fetch می‌کند. کلید هرگز به مرورگر نمی‌رسد.

مفاهیم پایه (کلید، فیلدها، خطاها) در راهنمای کامل API قیمت ارز و طلا آمده و اینجا فقط پیاده‌سازی را می‌بینیم.

معماری: چرا یک لایهٔ سرور لازم است؟

هر چیزی که در کامپوننت کلاینت React بنویسید، در فایل جاوااسکریپت به دست کاربر می‌رسد. اگر کلید API را آنجا بگذارید، هر کسی با باز کردن DevTools آن را می‌بیند. Next.js این مشکل را با Route Handler حل می‌کند: درخواست به API روی سرور شما انجام می‌شود و مرورگر فقط نتیجه را می‌بیند. یک مزیت دیگر هم دارد: با کش ۶۰ ثانیه‌ای، هزار بازدیدکنندهٔ هم‌زمان فقط یک درخواست به API می‌سازند. توضیح کامل‌تر در امنیت API Key در فرانت‌اند.

گام ۱: Route Handler سمت سرور

typescript
// app/api/gold/route.ts
import { NextResponse } from "next/server"

const SYMBOLS = "g18,g24,mesghal,ounce,emami,bahar,nim,rob"

export async function GET() {
  const res = await fetch(`https://api.ezrate.me/v1/rates?symbols=${SYMBOLS}&lang=fa`, {
    headers: { "X-API-Key": process.env.EZRATE_KEY! },
    next: { revalidate: 60 },          // کش سمت سرور: حداکثر یک درخواست در دقیقه
  })
  if (!res.ok) {
    return NextResponse.json({ error: res.status }, { status: 502 })
  }
  const { data, meta } = await res.json()
  return NextResponse.json({ data, as_of: meta.as_of })
}

کلید را در فایل .env.local با نام EZRATE_KEY بگذارید. چون نام آن با NEXT_PUBLIC_ شروع نمی‌شود، Next.js آن را هرگز وارد باندل مرورگر نمی‌کند.

گام ۲: کامپوننت تابلو

tsx
// app/components/GoldBoard.tsx
"use client"
import { useEffect, useState } from "react"

type Rate = { symbol: string; name: string; buy: number; sell: number; change_pct: number; quote: string }
const fmt = new Intl.NumberFormat("fa-IR")

export default function GoldBoard() {
  const [rates, setRates] = useState<Rate[]>([])
  const [asOf, setAsOf] = useState("")

  useEffect(() => {
    const load = () =>
      fetch("/api/gold").then(r => r.ok ? r.json() : null).then(j => {
        if (j) { setRates(j.data); setAsOf(j.as_of) }   // در خطا عدد قبلی می‌ماند
      })
    load()
    const t = setInterval(load, 60_000)
    return () => clearInterval(t)
  }, [])

  return (
    <table dir="rtl">
      <thead><tr><th>نوع</th><th>خرید</th><th>فروش</th><th>تغییر</th></tr></thead>
      <tbody>
        {rates.map(r => (
          <tr key={r.symbol}>
            <td>{r.name}</td>
            <td>{fmt.format(r.buy)}</td>
            <td>{fmt.format(r.sell)}</td>
            <td style={{ color: r.change_pct >= 0 ? "#0B8A55" : "#D1343F" }}>
              {r.change_pct >= 0 ? "▲" : "▼"} {fmt.format(Math.abs(r.change_pct))}٪
            </td>
          </tr>
        ))}
      </tbody>
      <caption>به‌روزرسانی: {asOf && new Date(asOf).toLocaleTimeString("fa-IR")}</caption>
    </table>
  )
}
تابلو قیمت طلا و سکه ساخته‌شده با React
نتیجهٔ نهایی کامپوننت تابلو در مرورگر

گام ۳: رندر سمت سرور برای سئو

کامپوننت بالا بعد از بارگذاری صفحه عدد را می‌گیرد، پس موتور جستجو اولین بار جدول خالی می‌بیند. اگر صفحهٔ شما باید در گوگل برای «قیمت طلا» دیده شود، نسخهٔ اول را در Server Component بسازید و فقط به‌روزرسانی‌های بعدی را به کلاینت بسپارید:

tsx
// app/gold/page.tsx — داده‌ی اولیه روی سرور، به‌روزرسانی روی کلاینت
async function getRates() {
  const r = await fetch(`https://api.ezrate.me/v1/rates?symbols=g18,emami&lang=fa`, {
    headers: { "X-API-Key": process.env.EZRATE_KEY! }, next: { revalidate: 60 } })
  return r.ok ? (await r.json()).data : []
}
export default async function Page() {
  const initial = await getRates()
  return <GoldBoardLive initial={initial} />   // همان کامپوننت با مقدار اولیه
}
✓
پیشنهاد
برای سایت‌هایی که فقط نمایش می‌خواهند، ساختن تابلو از صفر لازم نیست: ویجت قیمت EZRate با یک تگ script همین جدول را با نمودار و تم تیره می‌دهد. نسخهٔ انگلیسی یک مبدل ارز React هم در وبلاگ داریم.

خطاهای رایج

مشکلعلتراه حل
جدول خالی می‌ماندکلید در محیط production تعریف نشدهمتغیر EZRATE_KEY را در تنظیمات هاست هم بگذارید
خطای 429 در ساعت‌های شلوغکش غیرفعال استمطمئن شوید revalidate تعیین شده و cache: "no-store" نگذاشته‌اید
اعداد انگلیسی نمایش داده می‌شوندlocale تعیین نشدهاز Intl.NumberFormat("fa-IR") استفاده کنید
Hydration mismatchزمان یا عدد روی سرور و مرورگر فرق داردزمان را فقط بعد از mount رندر کنید
18K
طلای ۱۸ عیار۲۶٬۱۱۸٬۶۰۰تومان+۱٫۲۹٪

طراحی تابلو برای طلافروشی: چه ستون‌هایی لازم است؟

تابلویی که برای مشتری طلافروشی طراحی می‌شود با داشبورد یک تحلیلگر فرق دارد. مشتری دنبال سه چیز است: قیمت گرم طلای ۱۸ عیار، قیمت سکه‌ها و اینکه بازار امروز بالا رفته یا پایین. پیشنهاد ما:

ردیفنمادچرا
طلای ۱۸ عیار (گرم)g18مبنای قیمت زیورآلات
مثقال (آبشده)mesghalمرجع خرید و فروش عمده
انس جهانیounceجهت بازار جهانی
سکه امامی، بهار آزادیemami baharپرسش‌شده‌ترین سکه‌ها
نیم، ربع و گرمیnim rob geramiخرید کادویی

اگر قیمت را برای فروش خود اعلام می‌کنید، باید سود یا ضریب خودتان را روی نرخ بازار اعمال کنید. این همان کاری است که تابلو قیمت EZRate با «ضریب تعدیل» انجام می‌دهد؛ منطق آن را در نمایش نرخ اختصاصی صرافی کنار نرخ بازار خوانده‌اید یا می‌خوانید.

نمایش تغییر قیمت با افکت چشمک

یکی از جزئیات کوچکی که تابلو را «زنده» نشان می‌دهد، رنگی شدن لحظه‌ای ردیفی است که قیمتش عوض شده:

tsx
function Row({ r, prev }: { r: Rate; prev?: Rate }) {
  const dir = prev && r.sell !== prev.sell ? (r.sell > prev.sell ? "up" : "dn") : ""
  return (
    <tr key={`${r.symbol}-${r.sell}`} className={dir ? `flash-${dir}` : ""}>
      <td>{r.name}</td><td>{fmt.format(r.sell)}</td>
    </tr>
  )
}
/* CSS:
.flash-up { animation: up 1.2s }  @keyframes up { from { background:#E3F4EC } }
.flash-dn { animation: dn 1.2s }  @keyframes dn { from { background:#FBE7E8 } } */

مقدار قبلی هر ردیف را در یک useRef نگه دارید و هنگام دریافت دادهٔ جدید با آن مقایسه کنید. تغییر key ردیف باعث می‌شود انیمیشن در هر تغییر دوباره اجرا شود.

حالت تمام‌صفحه برای تلویزیون

اگر تابلو را روی تلویزیون مغازه نمایش می‌دهید، چند نکته فرق می‌کند: فونت باید از سه متری خوانا باشد (حداقل ۴۸ پیکسل برای قیمت)، رنگ پس‌زمینه تیره باشد تا در نور مغازه چشم را اذیت نکند و صفحه هرگز به حالت خواب نرود. برای جلوگیری از خواب صفحه در مرورگرهای جدید از Wake Lock API استفاده کنید:

javascript
useEffect(() => {
  let lock: any
  ;(navigator as any).wakeLock?.request("screen").then((l: any) => (lock = l)).catch(() => {})
  return () => lock?.release()
}, [])

اگر نمی‌خواهید این تابلو را خودتان توسعه و نگهداری کنید، راهنمای اتصال تلویزیون طلافروشی به قیمت لحظه‌ای روش بدون کدنویسی را توضیح می‌دهد.

تست قبل از انتشار

  • یک‌بار اینترنت را قطع کنید و ببینید تابلو آخرین عدد را با زمانش نگه می‌دارد.
  • کلید را موقتاً اشتباه کنید و مطمئن شوید پیام خطای مفهومی در لاگ سرور ثبت می‌شود.
  • صفحه را در موبایل باز کنید؛ جدول باید بدون اسکرول افقی خوانا باشد.
  • در DevTools تب Network را ببینید: هیچ درخواستی نباید مستقیم به api.ezrate.me برود.
تابلو قیمت طلا در حالت تمام‌صفحه روی تلویزیون
نسخهٔ تمام‌صفحهٔ تابلو برای تلویزیون مغازه

نرخ‌های لحظه‌ای مرتبط با این مطلب: قیمت طلای ۱۸ عیار، قیمت سکه امامی، قیمت دلار.

نمایش قیمت طلا در React بدون Next.js هم ممکن است؟
بله، اما باید یک سرور کوچک (Express، Cloudflare Worker یا هر بک‌اند دیگر) جای Route Handler قرار بگیرد تا کلید سمت مرورگر نرود.
هر چند ثانیه قیمت را به‌روز کنم؟
هر ۶۰ ثانیه برای تابلو کافی است. نرخ‌ها هر دقیقه محاسبه می‌شوند و درخواست بیشتر عدد تازه‌تری نمی‌دهد.
می‌توانم قیمت سکه را با حباب نشان دهم؟
بله. ارزش ذاتی سکه را از قیمت طلا و انس محاسبه کنید؛ روش آن در محاسبهٔ حباب سکه با API آمده است.
برای اپ موبایل React Native چه کنم؟
همان الگو: اپ از سرور شما بخواند نه مستقیم از API. راهنمای انگلیسی Flutter و React Native را ببینید.
عرفان فضلی
من در حوزه توسعه نرم‌افزار و برنامه‌نویسی فعالیت می‌کنم و به طراحی و پیاده‌سازی راهکارهای کاربردی و بهینه علاقه‌مندم. تمرکز من بر توسعه قابلیت‌های جدید، بهبود عملکرد سیستم‌ها و حل چالش‌های فنی با استفاده از فناوری‌های روز است. در مسیر کاری خود تلاش می‌کنم با یادگیری مستمر و به‌کارگیری روش‌های نوین برنامه‌نویسی، محصولات نرم‌افزاری کارآمد، پایدار و قابل‌اعتماد توسعه دهم.
همهٔ مطالب نویسنده ←

نظرات (۰)

نظرها پس از تأیید نمایش داده می‌شوند؛ برای ارسال، ورود به حساب لازم است.

مطالب مرتبط

API و برنامه‌نویسی
اسکرپ قیمت ارز یا API رسمی؟ مقایسهٔ فنی، حقوقی و هزینهٔ واقعی
API و برنامه‌نویسی
دریافت نرخ ارز در PHP و لاراول با cURL و Guzzle؛ آموزش کامل
API و برنامه‌نویسی
نمایش قیمت ارز در وردپرس؛ آموزش با افزونه، شورت‌کد و API