پکیج 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، ipInfo | ip-info |
search_shazam | search-shazam |
chatAi، ChatAi | chat-ai |
chatAI | chat-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 / false | x=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() بدون هیچ تنظیمی کار میکند. همهٔ گزینهها در سازنده تنظیم میشوند.
| گزینه | پیشفرض | توضیح |
|---|---|---|
version | 1 | نسخهٔ API؛ مقدار N مسیر apis-N را میسازد |
baseUrl | https://open.nestapi.ir | آدرس پایه |
timeout | 30000 | زمان انتظار به میلیثانیه، فقط برای fetch پیشفرض |
fetch | globalThis.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" }) استفاده کنید.