به محتوای اصلی

SDK جاوااسکریپت فیکارو؛ اتصال اپ به API بدون نوشتن لایه‌ی شبکه

کلاینت رسمی فیکارو برای جاوااسکریپت و تایپ‌اسکریپت: نصب با npm، فیلتر و صفحه‌بندی، ورود کاربران و تمدید خودکار توکن — برای وب، Node و ری‌اکت‌نیتیو.

عاطفه امیری۴ دقیقه مطالعه
در این مطلب

هر API‌ای بالاخره باید از یک اپ صدا زده شود، و آن‌جاست که کارِ تکراری شروع می‌شود: ساختن URL، چسباندن هدر توکن، تشخیص اینکه خطا چه بود، و آن لحظه‌ای که توکن وسط کار منقضی می‌شود. این مقاله می‌گوید این کارها دقیقاً کجا اشتباه از آب درمی‌آیند، و چطور با @fikaro/client — کلاینت رسمی، متن‌باز و بدون وابستگی — از سرشان رد شوید.

چهار چیزی که نوشتن دستی کلاینت را خراب می‌کند

اگر تا حالا یک لایه‌ی API با fetch خام نوشته‌اید، احتمالاً هر چهارتا را دیده‌اید.

انکود کردن پارامترها. یک فیلتر ساده مثل «قیمت بیشتر از ۵۰» در URL این شکلی می‌شود:

?filter%5Bprice%5D%5Bgt%5D=50

نوشتن دستی‌اش جواب می‌دهد تا روزی که مقدار فیلتر یک رشته‌ی فارسی یا یک تاریخ باشد.

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

فهمیدن اینکه خطا چه بود. catch (e) که فقط یک رشته دارد، به شما نمی‌گوید مشکل نامعتبر بودن یک فیلد بوده یا تمام شدن سهمیه. برای نشان دادن پیام درست زیر همان فیلدِ فرم، به ساختار نیاز دارید نه متن.

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

نصب و اولین درخواست

همین. from یک دسته را برمی‌گرداند و list یک صفحه از رکوردها. فیلترها به‌صورت آبجکت نوشته می‌شوند و کتابخانه خودش به شکل درست تبدیلشان می‌کند.

فیلترها

شرط‌ها با AND ترکیب می‌شوند و مقدار خام یعنی «برابر است با»:

await client.from("product").list({
  filter: {
    status: "active",              // برابر
    price: { gte: 50, lt: 500 },   // بازه
    name: { like: "%قهوه%" },      // جست‌وجوی متنی
  },
  sort: "-createdAt",              // نزولی
  limit: 20,
});

عملگرهای مجاز: eq، ne، gt، gte، lt، lte، like و in.

صفحه‌بندی بدون جا انداختن رکورد

به‌جای شماره‌ی صفحه، هر پاسخ یک نشانگر (cursor) می‌دهد که می‌گوید «از این‌جا ادامه بده». چون به موقعیت عددی وابسته نیست، افزوده شدن رکورد وسط کار چیزی را خراب نمی‌کند:

let cursor;
do {
  const page = await client.from("order").list({ limit: 100, cursor });
  process(page.data);
  cursor = page.pagination.nextCursor ?? undefined;
} while (cursor);

یا اجازه دهید خودش دنبال کند. چون ژنراتور است، با break می‌شود وسط کار ایستاد و بقیه اصلاً دانلود نمی‌شود:

for await (const order of client.from("order").listAll({ limit: 100 })) {
  if (order.total > 10_000_000) break;
}

ورود کاربران اپ شما

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

توکن منقضی‌شده خودکار تازه می‌شود: یک ۴۰۱ باعث یک بار رفرش می‌شود و درخواست اصلی دوباره فرستاده می‌شود. درخواست‌های هم‌زمان یک رفرش مشترک دارند — همان باگی که بالاتر گفتم، این‌جا از پیش حل شده است.

جزئیات JWT و سطح دسترسی را در مقاله‌ی احراز هویت با JWT نوشته‌ایم.

توکن را کجا نگه داریم؟

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

این عمدی است. ذخیره‌ی توکن یک تصمیم امنیتی با پیامد واقعی است و باید در کد خودتان دیده شود، نه اینکه بی‌صدا از یک کتابخانه به ارث برسد:

import { createClient, browserStorage } from "@fikaro/client";

const client = createClient({
  project: "my-app",
  storage: browserStorage(),   // ماندگار بین رفرش‌ها
});

localStorage برای هر اسکریپتی که روی صفحه اجرا شود خواندنی است، پس یک آسیب‌پذیری XSS یعنی یک رفرش‌توکن دزدیده‌شده. برای یک اپ کم‌ریسک قابل قبول است؛ برای چیزی که پرداخت یا اطلاعات شخصی دارد نه. در آن حالت TokenStorage را خودتان پیاده کنید — کوکی httpOnly از سمت سرور خودتان، یا expo-secure-store و کی‌چین سیستم‌عامل در موبایل.

خطاها

خطاها به یک رشته تبدیل نمی‌شوند. سرور استاندارد RFC 7807 حرف می‌زند و همان ساختار تا دست شما می‌رسد:

import { FikaroError, FikaroNetworkError } from "@fikaro/client";

try {
  await client.from("product").create({ price: -1 });
} catch (err) {
  if (err instanceof FikaroError) {
    err.code;                   // "validation_failed"
    err.fieldError("price");    // پیام همان فیلد، برای نشان دادن زیر فرم
    err.isRateLimited;          // ۴۲۹ — سهمیه یا محدودیت نرخ
  } else if (err instanceof FikaroNetworkError) {
    // اصلاً به سرور نرسید: قطعی شبکه یا تایم‌اوت. این یکی ارزش تلاش دوباره دارد.
  }
}

یک نکته: روی err.code شرط بگذارید نه روی متن پیام. متن‌ها فارسی و قابل تغییرند؛ کد پایدار است.

کدام کلید را کجا بگذاریم

| پیشوند | جایش کجاست | | --- | --- | | apck_pub_ | عمومی. فقط خواندنی و محدود به موجودیت‌هایی که صریحاً عمومی کرده‌اید. امن برای باندل مرورگر و فایل نصبی موبایل. | | apck_prod_ و apck_play_ | محرمانه. فقط سمت سرور. هرچه به دستگاه کاربر برسد عمومی است، هر کاری با باندلر بکنید. |

این با SDKهای فلاتر و کاتلین چه فرقی دارد؟

هر دو وجود دارند و هدفشان یکی نیست.

SDKهای Flutter و Kotlin و Swift از روی اسکیمای پروژه‌ی شما تولید می‌شوند و مدل‌ها را تایپ می‌کنند. هر بار مدل داده را عوض کردید، نسخه‌ی تازه را از پنل بگیرید.

@fikaro/client تولید نمی‌شود، نصب می‌شود. با تغییر اسکیما نیازی به دانلود دوباره ندارد، ولی مدل‌ها را هم خودکار نمی‌سازد — تایپ را خودتان می‌دهید:

interface Product { id: string; name: string; price: number }
const products = client.from<Product>("product");

انتخاب بین این دو به این برمی‌گردد که ترجیح می‌دهید تایپ‌ها خودکار باشند و هر بار دانلود کنید، یا کتابخانه ثابت بماند و تایپ‌ها دست خودتان باشد.

کجا کار می‌کند

Node نسخه‌ی ۱۸ به بالا، همه‌ی مرورگرهای امروزی، ری‌اکت‌نیتیو، Deno، Bun و Cloudflare Workers. تنها نیازش وجود fetch سراسری است.

کد کامل با لایسنس MIT روی گیت‌هاب است — می‌توانید بخوانیدش، ایراد بگیرید، یا فورکش کنید.

سوالات متداول

برای استفاده از SDK حتماً باید تایپ‌اسکریپت بنویسم؟
نه. کتابخانه با جاوااسکریپت ساده هم کار می‌کند و تایپ‌ها اختیاری‌اند. اگر تایپ‌اسکریپت بنویسید، تعریف تایپ‌ها از قبل داخل پکیج هست و نیازی به نصب چیز اضافه‌ای نیست.
کلید عمومی را می‌شود داخل کد سمت کلاینت گذاشت؟
بله، کلید با پیشوند apck_pub_ دقیقاً برای همین ساخته شده: فقط خواندنی است و فقط به موجودیت‌هایی دسترسی دارد که صریحاً عمومی کرده‌اید. کلیدهای apck_prod_ و apck_play_ محرمانه‌اند و باید سمت سرور بمانند.
اگر توکن کاربر وسط کار منقضی شود چه اتفاقی می‌افتد؟
کتابخانه خودش یک بار توکن را تازه می‌کند و درخواست اصلی را دوباره می‌فرستد. اگر چند درخواست هم‌زمان با توکن منقضی روبه‌رو شوند، همه یک رفرش مشترک را استفاده می‌کنند تا رفرش‌توکن چرخشی باطل نشود.
این SDK جای SDK فلاتر و کاتلین را می‌گیرد؟
نه، مکمل آن‌هاست. SDKهای Flutter و Kotlin و Swift از روی اسکیمای پروژه تولید می‌شوند و مدل‌ها را تایپ می‌کنند. کلاینت جاوااسکریپت نصب می‌شود و برای وب، Node و ری‌اکت‌نیتیو است.
کد SDK متن‌باز است؟
بله، با لایسنس MIT روی گیت‌هاب در مخزن fikaro-platform/fikaro-js. موتور خود فیکارو بسته است، ولی این لایه‌ی کلاینت به‌هرحال در مرورگر کاربر اجرا می‌شود و باز بودنش چیزی را افشا نمی‌کند.

مطالب مرتبط