پرش به مطلب اصلی

پکیج JavaScript

پکیج رسمی @devh3n/nestapi یک کلاینت پویا و سبک برای JavaScript و TypeScript است. نام هیچ سرویسی داخل پکیج نیست: هر متدی که صدا بزنید به مسیر همان سرویس تبدیل می‌شود، پس سرویس‌های جدید بدون به‌روزرسانی پکیج در دسترس هستند.

  • بدون وابستگی؛ بر پایهٔ fetch و قابل استفاده در Node.js و مرورگر
  • همهٔ درخواست‌ها Promise برمی‌گردانند
  • نوشته‌شده با TypeScript، با خروجی ESM و CommonJS

نصب​

npm install @devh3n/nestapi

یا با pnpm و yarn:

pnpm add @devh3n/nestapi
yarn add @devh3n/nestapi

نیازمندی‌ها: Node.js 18 به بالا، یا مرورگری که fetch و AbortSignal.timeout را پشتیبانی کند.

شروع سریع​

ES Modules:

import { NestApi } from "@devh3n/nestapi";

const api = new NestApi();
const result = await api.bmi({ age: 30, weight: 70, height: 175 });

console.log(result.data.bmi); // 22.86
console.log(result.get("input.age")); // 30

CommonJS:

const { NestApi } = require("@devh3n/nestapi");

async function main() {
const api = new NestApi();
const result = await api.bmi({ age: 30, weight: 70, height: 175 });

console.log(result.data.bmi); // 22.86
}

main();

تبدیل نام متد به مسیر​

مسیر از نام متد ساخته می‌شود: هر _ به - تبدیل می‌شود، هر حرف بزرگ (به‌جز حرف اول) یک بخش جدید با - شروع می‌کند و نتیجه با حروف کوچک ارسال می‌شود.

متدمسیر
ip_info، ipInfoip-info
search_shazamsearch-shazam
chatAi، ChatAichat-ai
chatAIchat-a-i

صرفِ خواندن متد درخواستی نمی‌فرستد؛ درخواست با فراخوانی آن ارسال می‌شود.

ارسال پارامترها​

پارامترها را به شکل یک آبجکت ساده بفرستید، یا اصلاً نفرستید. هر چیز دیگری با TypeError رد می‌شود و درخواستی ارسال نمی‌شود. این خطاها همیشه به شکل Promise ردشده برمی‌گردند، پس catch و try/await یکسان رفتار می‌کنند.

await api.bmi({ age: 30, weight: 70, height: 175 });
await api.randomQuote();

await api.bmi(30, 70); // TypeError
await api.bmi([30, 70]); // TypeError
await api.bmi(null); // TypeError
await api.bmi(new Map()); // TypeError

فراخوانی با نام دقیق مسیر​

برای مسیرهایی که با نام متد JavaScript قابل نوشتن نیستند، مثل 4jok، از call() استفاده کنید. این متد نام مسیر را بدون تغییر می‌فرستد.

// درخواست GET با نام دقیق مسیر و بدون تبدیل
await api.call("4jok");
await api.call("ip-info", { ip: "8.8.8.8" });

// هر متد HTTP دیگر؛ پارامترها همچنان در Query String قرار می‌گیرند
await api.request("POST", "some-service", { a: 1 });
یادداشت

سرویس‌هایی با نام call، request، version، then، toJSON یا هم‌نام با اعضای Object.prototype (مثل constructor، toString، valueOf و hasOwnProperty) و نام‌هایی که با _ شروع می‌شوند، به شکل متد در دسترس نیستند. آن‌ها را با call() صدا بزنید، برای مثال api.call("version"). به لطف همین استثناها، await api و JSON.stringify(api) درخواستی نمی‌فرستند.

نحوهٔ ارسال مقدارها​

مقدار JavaScriptارسال به شکلتوضیح
true / falsex=true / x=falseسرور مقدارهای 1 و 0 را نمی‌پذیرد
null / undefinedارسال نمی‌شودداخل آرایه و آبجکت هم نادیده گرفته می‌شود
0، ""x=0، x=همیشه ارسال می‌شوند
آرایهx=1&x=2کلیدهای تکراری
آبجکتx[key]=1همان قالب پکیج‌های PHP و Python

فاصله به شکل %20 و متن غیرانگلیسی با UTF-8 ارسال می‌شود. مقدارهایی که شکل Query String ندارند، مثل Date، Map، تابع یا Symbol، با TypeError رد می‌شوند؛ آن‌ها را اول به رشته تبدیل کنید.

نسخه‌ها​

نسخهٔ پیش‌فرض 1 است و پیشوند apis- خودکار اضافه می‌شود. نسخهٔ هر سرویس در مستندات همان سرویس آمده است. متد version() یک کپی با همان تنظیمات برمی‌گرداند و نمونهٔ اصلی تغییر نمی‌کند. این متد همگام است، پس خطاهایش مستقیم throw می‌شوند.

const api = new NestApi({ version: 2 });

const v3 = api.version(3);

await v3.bmi({ age: 30, weight: 70, height: 175 }); // /apis-3/bmi
await api.bmi({ age: 30, weight: 70, height: 175 }); // /apis-2/bmi

عدد صحیح کمتر از 1 خطای RangeError و هر مقدار غیرصحیح، مثل 1.5، NaN، "1" یا "apis-1"، خطای TypeError می‌دهد. فیلد source.version در پاسخ ربطی به نسخهٔ داخل آدرس ندارد.

خواندن پاسخ​

هر فراخوانی موفق یک شیء فقط‌خواندنی ApiResponse برمی‌گرداند.

عضوتوضیح
result.dataمقدار فیلد data؛ آبجکت، آرایه، رشته یا null
result.metaمقدار فیلد meta یا null
result.sourceمقدار فیلد source
result.get("a.b", defaultValue)خواندن از داخل data با مسیر نقطه‌ای
result.has("key")بررسی وجود کلید سطح اول (یا اندیس آرایه) در data

مسیر get() از داخل data شروع می‌شود و می‌تواند وارد آرایه‌ها هم بشود. پاسخ به‌صورت عمیق فریز شده و قابل تغییر نیست.

const result = await api.bmi({ age: 30, weight: 70, height: 175 });

result.data.bmi // 22.86
result.get("input.age") // 30
result.get("input.nope", 0) // 0
result.get("items.0.title") // اندیس آرایه در مسیر

result.has("bmi") // true
result.has("nope") // false

result.data = {} // TypeError در strict mode
result.data.bmi = 1 // TypeError در strict mode
  • کلیدی که وجود دارد ولی مقدارش null است، null برمی‌گرداند نه مقدار پیش‌فرض.
  • اگر data آبجکت یا آرایه نباشد، get() مقدار پیش‌فرض و has() مقدار false برمی‌گرداند.
  • خروجی JSON.stringify(result) شامل data، meta و source است.

مدیریت خطا​

پاسخ‌های ناموفق به خطا (reject) تبدیل می‌شوند. برای شرط‌نویسی از errorCode استفاده کنید، نه متن پیام، چون متن پیام ممکن است تغییر کند.

کلاسزمان رخ دادن
ServiceNotFoundErrorکد NOT_FOUND؛ سرویس یا نسخهٔ ناموجود
ValidationErrorکدهای PARAM_NOT_FOUND و VALIDATION_ERROR
RateLimitErrorکدهای RATE_LIMITED و RATE_LIMIT_EXCEEDED
ServerErrorسایر کدها با وضعیت ۵۰۰ و بالاتر
ApiErrorکلاس پایهٔ موارد بالا و سایر کدها مثل INVALID_IP
TransportErrorخطای شبکه، پایان زمان انتظار، بدنهٔ غیر JSON یا پاسخ خارج از قالب API (از ApiError مستقل است)
import { ApiError, TransportError, ValidationError } from "@devh3n/nestapi";

try {
await api.bmi({ age: 500, weight: 70, height: 175 });
} catch (error) {
if (error instanceof ValidationError) {
error.errorCode; // "VALIDATION_ERROR"
error.status; // 422
error.details; // { "query.age": "Must be less than or equal to 120" }
} else if (error instanceof ApiError) {
// سایر خطاهای گزارش‌شده توسط API
} else if (error instanceof TransportError) {
// پاسخ قابل استفاده‌ای نرسید؛ خطای اصلی در error.cause است
}
}

مقدار details برای PARAM_NOT_FOUND یک آرایه و برای VALIDATION_ERROR یک آبجکت است.

پیکربندی​

new NestApi() بدون هیچ تنظیمی کار می‌کند. همهٔ گزینه‌ها در سازنده تنظیم می‌شوند.

گزینهپیش‌فرضتوضیح
version1نسخهٔ API؛ مقدار N مسیر apis-N را می‌سازد
baseUrlhttps://open.nestapi.irآدرس پایه
timeout30000زمان انتظار به میلی‌ثانیه، فقط برای fetch پیش‌فرض
fetchglobalThis.fetchیک تابع fetch دلخواه
const api = new NestApi({
version: 1,
baseUrl: "https://open.nestapi.ir",
timeout: 30000,
fetch: myFetch,
});

fetch تزریق‌شده زمان انتظار خودش را دارد، پس timeout روی آن اثری ندارد. بررسی SSL به fetch سپرده شده و قابل غیرفعال کردن نیست. متد close() هم وجود ندارد، چون fetch اتصالی برای بستن در اختیار کلاینت نمی‌گذارد.

TypeScript​

تایپ‌ها همراه پکیج هستند. هر نام سرویس به شکل متد تایپ می‌شود و متدهای عمومی امضای خودشان را دارند.

import { NestApi, type ApiResponse } from "@devh3n/nestapi";

const api = new NestApi();

const result: ApiResponse = await api.ipInfo({ ip: "8.8.8.8" });

نوع data و meta برابر any است، چون شکلشان به سرویس بستگی دارد.

یادداشت

نام‌های پویا بر پایهٔ index signature هستند، پس دو گزینهٔ کامپایلر noUncheckedIndexedAccess و noPropertyAccessFromIndexSignature آن‌ها را رد می‌کنند. با فعال بودن هر یک، از api.call("ip-info", { ip: "8.8.8.8" }) استفاده کنید.

لینک‌های بیشتر​