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

فانکشن‌ها

قوانین روی نوشتن و خواندن یک موجودیت می‌نشینند. فانکشن جای کاری است که به هیچ موجودیتی وصل نیست: یک آدرس HTTP که خودتان نامش را می‌گذارید و داخلش جاوااسکریپت می‌نویسید.

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

آدرس

POST https://api.fikaro.ir/{اسلاگ-پروژه}/v1/fn/{نام-فانکشن}

GET هم کار می‌کند. متدهای دیگر ۴۰۵ می‌گیرند.

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

سطح دسترسی

هر فانکشن یکی از این سه را دارد. پیش‌فرض بسته‌ترین است، چون باز کردنش یک تصمیم آگاهانه می‌خواهد:

| سطح | چه کسی می‌تواند صدا بزند | |---|---| | فقط کلید API (پیش‌فرض) | سرور خودتان، با کلید پروداکشن یا پلی‌گراند | | + کاربران واردشده | علاوه بر آن، اپ کلاینت با توکن کاربر | | عمومی | با کلید publishable، و حتی بدون احراز هویت |

«عمومی» برای فرم تماس یا وب‌هوک درگاه درست است — جایی که فرستنده کلید ندارد. برای بقیه، پیش‌فرض را نگه دارید.

آنچه کد شما در اختیار دارد

داخل فانکشن یک شیء ctx دارید. هیچ awaitی لازم نیست؛ همه‌چیز هم‌زمان جواب می‌دهد.

درخواست و پاسخ

const { method, path, query, body, headers } = ctx.request;

ctx.respond({ ok: true });        // ۲۰۰ با این بدنه
ctx.respond(201, { id: rec.id }); // با کد وضعیت دلخواه

ctx.respond اجرا را همان‌جا تمام می‌کند — خطی که بعدش بنویسید اجرا نمی‌شود.

برای رد کردن با خطا:

if (!ctx.request.body.email) {
  ctx.reject("ایمیل الزامی است");
}

دیتابیس، با دو دسته

const rows = ctx.db.list("order", { limit: 20 });
const one  = ctx.db.get("order", id);
ctx.db.create("order", { total: 1000 });
ctx.db.update("order", id, { status: "paid" });
ctx.db.delete("order", id);

ctx.db با دسترسی صداکننده کار می‌کند: اگر کاربری با توکن خودش آمده و موجودیت روی «فقط رکوردهای خود کاربر» تنظیم شده، همان محدودیت اینجا هم برقرار است.

ctx.db.admin همان عملیات را با دسترسی کامل شما انجام می‌دهد و قوانین دسترسی را دور می‌زند. برای کاری مثل «شمارش کل سفارش‌ها» لازم است — ولی هر جا نوشتیدش، یعنی صریحاً گفته‌اید این خط از طرف پروژه اجرا می‌شود، نه از طرف کاربر. کم استفاده‌اش کنید.

چه کسی صدا زده

ctx.auth  // "key" | "user" | "publishable" | "anonymous"
ctx.user  // { id, role } — فقط وقتی با توکن کاربر آمده باشد

روی یک فانکشن عمومی، ctx.auth تنها راه تشخیص است. اگر رفتار متفاوتی برای کاربر واردشده می‌خواهید، همین‌جا شاخه بزنید.

سرویس بیرونی و secrets

const res = ctx.http.post(
  "https://example.com/api",
  ctx.request.body,
  { headers: { Authorization: "Bearer " + ctx.secrets.PARTNER_KEY } }
);
if (!res.ok) {
  ctx.respond(502, { error: "سرویس مقصد جواب نداد" });
}
ctx.respond(res.json);

ctx.secrets مقادیر محرمانه‌ی پروژه است. در پاسخ و لاگ نمی‌آیند مگر خودتان بنویسیدشان — پس کلید را در پیام خطا برنگردانید.

secrets را در پنل تعریف می‌کنید، از دو جا: تنظیمات پروژه ← کارت «secrets پروژه»، یا دکمهٔ «secrets» بالای فهرست فانکشن‌ها در همین صفحه. نام انگلیسی است (مثل SMS_PANEL_KEY) و مقدار بعد از ذخیره دیگر نشان داده نمی‌شود؛ فقط می‌شود عوضش کرد یا حذفش کرد. مثال رایج: کلید پنل پیامک یا درگاه پرداخت خودتان، که فانکشن با ctx.http صدایش می‌زند.

اشکال‌زدایی

ctx.log("رسید:", ctx.request.body);

خروجی ctx.log فقط در کنسول اجرای آزمایشی پنل دیده می‌شود و هرگز در پاسخ API نمی‌آید.

یک مثال کامل

فرم تماس: عمومی، بدون کلید، با اعتبارسنجی و ثبت رکورد.

const { name, email, message } = ctx.request.body || {};

if (!email || !message) {
  ctx.reject("ایمیل و متن پیام الزامی است");
}
if (message.length > 2000) {
  ctx.reject("متن پیام طولانی است");
}

const rec = ctx.db.admin.create("contact_message", {
  name: name || "",
  email: email,
  message: message,
  source: ctx.auth,
});

ctx.respond(201, { id: rec.id });

ctx.db.admin اینجا لازم است چون صداکننده ناشناس است و اجازه‌ی نوشتن مستقیم ندارد — و همین یک خط، مرز بین «هر کسی می‌تواند پیام بفرستد» و «هر کسی می‌تواند در جدول بنویسد» را نگه می‌دارد.

محدودیت‌ها

  • بدنه‌ی درخواست تا ۱ مگابایت. فانکشن آرگومان می‌گیرد، نه فایل؛ برای بایت‌ها API فایل‌ها هست.
  • عمق فراخوانی ۳. فانکشنی که آدرس خودش را صدا بزند (یا حلقه‌ای از سرویس‌ها که برگردد اینجا) در عمق سوم قطع می‌شود.
  • فقط GET و POST.

بیشتر بخوانید