پایدار شدن Fetch API در Node.js یکی از مورد انتظارترین بهروزرسانیها در سالهای اخیر بوده است. این ویژگی یک روش استاندارد و مدرن برای ارسال درخواستهای HTTP در محیط مرورگر و سرور فراهم میکند. برای درک بهتر اهمیت این موضوع، تاریخچه درخواستهای HTTP، نحوه پیدایش Fetch و تأثیر پایدار شدن آن بر توسعهدهندگان Node را در این مقاله بررسی میکنیم.
در روزهای اولیه وب، ارسال درخواستهای asynchronous بین وبسایتها کار دشواری بود و توسعهدهندگان مجبور بودند از روشهای پیچیدهای برای تعامل با شبکههای مختلف استفاده کنند.
در سال ۱۹۹۸، اینترنت اکسپلورر ۵ با معرفی API جدیدی به نام XMLHttpRequest
این مشکل را تغییر داد. این API در ابتدا برای دریافت دادههای XML از طریق HTTP طراحی شده بود و نام آن نیز از همین قابلیت گرفته شده است. اما پس از مدتی، پشتیبانی از فرمتهای دیگر مانند JSON، HTML و plaintext نیز به آن اضافه گردید.
XMLHttpRequest
در زمان خود عملکرد خوبی داشت، اما با رشد وب، کار کردن با آن به قدری دشوار شد که فریمورکهای جاوااسکریپت، به ویژه jQuery، مجبور شدند آن را به صورت یک لایه انتزاعی درآورند تا استفاده از آن آسانتر شود.
در سال ۲۰۱۵، Fetch API به عنوان جایگزینی مدرن برای XMLHttpRequest
معرفی شد و به سرعت به استاندارد اصلی برای ارسال درخواستهای asynchronous در برنامههای وب تبدیل گردید. یکی از مهمترین مزایای Fetch نسبت به XMLHttpRequest
استفاده از promiseها است که باعث سادگی و خوانایی بیشتر کد شده و از callback hell جلوگیری میکند.
با وجود اینکه Fetch API مدتی است که در دنیای وب وجود دارد، اما به دلیل برخی محدودیتها به طور پیشفرض در Node.js قرار نگرفته بود. یکی از توسعهدهندگان هسته Node توضیح داده است که پیادهسازی Fetch در مرورگر به Web Streams API و AbortController
وابسته است، در حالی که این ویژگیها تا همین اواخر در Node.js در دسترس نبودند. به همین دلیل، انتخاب بهترین روش برای افزودن Fetch به هسته Node چالشبرانگیز بود.
پیش از اضافه شدن Fetch API، محبوبترین روش برای ارسال درخواستهای HTTP در Node.js استفاده از ماژول request بود. اما با پیشرفت اکوسیستم جاوااسکریپت، این ماژول منسوخ شد. یکی از دلایل اصلی این اتفاق، عدم پشتیبانی از async/await
بود که در نهایت باعث شد این پروژه از بین برود.
در سال ۲۰۱۸، کتابخانه Undici معرفی شد که یک کلاینت HTTP/1.1 جدید و سریع برای Node.js بود. این کتابخانه قابلیتهایی مانند pipelining و pooling را ارائه میداد و تیم توسعهدهنده Node.js روی بهبود عملکرد و پایداری آن کار کردند.
Undici به عنوان پایهای برای پیادهسازی native fetch()
در Node.js عمل کرد و یک راهکار سریع و استاندارد برای ارسال درخواستهای HTTP فراهم نمود. با ادغام Undici در هسته Node.js، توسعهدهندگان به یک کلاینت HTTP قوی و سریع دسترسی پیدا کردند که زمینه را برای اضافه شدن Fetch API در نسخه ۱۸ Node فراهم کرد و در نهایت در نسخه ۲۱ پایدار شد.
همانطور که اشاره کردیم، Fetch API در نسخه ۱۸ به Node.js اضافه شد، اما تا قبل از نسخه ۲۱ همچنان در مرحله آزمایشی قرار داشت و مشکلات و باگهایی داشت که باعث ناپایداری آن میشد. انتشار نسخه پایدار در نسخه ۲۱ Node.js یک نقطه عطف بزرگ برای توسعهدهندگان محسوب میشود، زیرا این API اکنون به طور کامل تست شده و در شرایط مختلف عملکرد قابل اعتمادی دارد.
Fetch API یک تابع سطح بالا است و نیازی به import
یا require
برای استفاده از آن در برنامههای Node.js ندارد.
در سادهترین حالت، fetch یک URL را دریافت کرده و درخواست GET
را به آن ارسال میکند. نتیجه این درخواست یک promise است که پاسخ را برمیگرداند. به عنوان مثال:
fetch("http://example.com/api/endpoint") .then((response) => { // Do something with response }) .catch((err) => { // Handle error here console.log("Unable to fetch -", err); });
همچنین میتوانیم با ارسال یک آبجکت اختیاری، گزینههای بیشتری مانند نوع درخواست، هدرها و سایر تنظیمات را مشخص کنیم:
fetch("http://example.com/api/endpoint", { method: "POST", // Specify request method headers: { // Customize request header here "Content-Type": "application/json", // . . . }, body: JSON.stringify({ foo: "bar", bar: "foo", }), // . . . }) .then((res) => res.json()) .then((data) => { console.log("Response data:", data); }) .catch((err) => { console.log("Unable to fetch -", err); });
در این مثال، یک درخواست POST
به یک API ارسال شده و دادههای foo
و bar
در body ارسال میشوند. همچنین، هدر درخواست تنظیم شده است تا نوع محتوا application/json
باشد. سپس، پاسخ به JSON تبدیل شده و در کنسول نمایش داده میشود.
پیشفرض بودن Fetch API در Node.js مزایای زیادی برای توسعهدهندگان دارد، از جمله:
با درونسازی Fetch API در Node.js، دیگر نیازی به استفاده از پکیجهایی مانند node-fetch ،got و cross-fetch وجود ندارد. این یعنی توسعهدهندگان بدون نیاز به نصب ماژولهای اضافی میتوانند عملیات شبکهای را در Node انجام دهند.
علاوه بر این، پکیج node-fetch که محبوبترین گزینه برای استفاده از Fetch در Node.js بود، اخیراً به یک پکیج ESM-only تبدیل شده است که دیگر نمیتوانیم آن را با تابع require()
در Node استفاده کنیم. این موضوع باعث شده است تا استفاده از native Fetch API در محیط Node بسیار راحتتر و طبیعیتر شود.
توسعهدهندگانی که قبلاً از Fetch API در فرانتاند استفاده میکردند، اکنون میتوانند از همان API در Node نیز بهرهمند شوند. این هماهنگی باعث میشود که کار با درخواستهای HTTP در محیط Node سادهتر و شهودیتر از گذشته باشد.
همانطور که پیشتر به آن اشاره کردیم ، پیادهسازی جدید Fetch نیز بر پایهی Undici است. در نتیجه، میتوان انتظار داشت که عملکرد API مربوط به Fetch نیز بهبود یابد.
Fetch API مرورگر ذاتاً دارای برخی محدودیتهاست و این محدودیتها به پیادهسازی جدید Node.js نیز منتقل خواهند شد:
Fetch API در Node.js از progress eventها در هنگام بارگذاری یا دانلود فایل پشتیبانی نمیکند. توسعهدهندگانی که به کنترل دقیق بر فرآیند نظارت بر پیشرفت نیاز دارند، ممکن است با این محدودیت مواجه شوند و ناچار باشند از کتابخانهها یا راهحلهای جایگزین استفاده کنند.
در حالی که Fetch API در نسخههای جدیدتر Node.js پایدار است، پروژههایی که از نسخههای قدیمیتر استفاده میکنند ممکن است با مشکلات سازگاری روبهرو شوند. توسعهدهندگانی که در محیطهایی کار میکنند که امکان بهروزرسانی Node.js در آنها وجود ندارد، احتمالاً مجبور خواهند شد از کتابخانههای جایگزین استفاده کنند یا راهحلهای دیگری بیابند.
رویکرد Fetch API در مدیریت کوکیها بر اساس رفتار مرورگر است، که ممکن است در محیط Node.js نتایج غیرمنتظرهای ایجاد کند. هنگام کار با کوکیها، توسعهدهندگان باید دقت لازم را داشته باشند و تنظیمات را متناسب با موارد استفاده خاص پیکربندی کنند.
بررسی انتقال به Fetch رسمی و پایدار
ارتقا به نسخهی رسمی و پایدار Fetch در Node.js فرآیندی نسبتاً ساده دارد که در ادامه آن را بررسی میکنیم:
ابتدا باید مطمئن شویم که نسخه Node.js ما حداقل نسخه ۱۲ یا بالاتر باشد. برای بررسی نسخه فعلی، میتوانیم از دستور زیر استفاده کنیم:
node -v
اگر نسخه Node.js ما پایینتر از ۲۱ است، باید آن را به جدیدترین نسخه ارتقا دهیم. میتوانیم آخرین نسخه را مستقیماً از وبسایت رسمی Node.js دانلود کنیم یا از یک Node Version Manager مانند nvm استفاده کنیم. به عنوان مثال، برای مشاهده تمامی نسخههای در دسترس Node.js میتوانیم از دستور زیر استفاده کنیم:
nvm ls-remote
پس از مشاهده نسخه مورد نظر، میتوانیم آن را با دستور زیر نصب نماییم:
nvm install x.y.z # E.g to install v22.13.0: nvm install v22.13.0
اگر پیش از این از یک کتابخانه خارجی برای ارسال درخواست در Node.js استفاده میکردیم (مانند node-fetch
یا یک کتابخانه HTTP سفارشی دیگر)، حالا که Fetch API به طور native در دسترس است، میتوانیم این وابستگیها را حذف کنیم:
npm uninstall node-fetch
بهروزرسانی کد برای استفاده از Fetch native
کدهایی که در حال حاضر از یک کتابخانه خارجی استفاده میکنند را با پیادهسازی native fetch
جایگزین میکنیم. حتماً باید نحوه نگارش کد را بهروزرسانی کرده و مدیریت صحیح Promiseها را در نظر بگیریم.
قبل از بهروزرسانی:
const fetch = require("node-fetch"); fetch("https://api.example.com/data") .then((response) => response.json()) .then((data) => console.log(data)) .catch((error) => console.error("Error:", error));
بعد از بهروزرسانی بهFetch native:
fetch("https://api.example.com/data") .then((response) => response.json()) .then((data) => console.log(data)) .catch((error) => console.error("Error:", error));
با این تغییرات، کد ما سادهتر شده و دیگر نیازی به استفاده از وابستگیهای خارجی نخواهد داشت.
بر اساس نیازهایی که داریم، میتوانیم گزینهها و هدرهای درخواستهای خود را بررسی و تنظیم کنیم. native Fetch API ممکن است در رفتار خود تفاوتهایی داشته باشد یا قابلیتهای اضافی ارائه دهد، بنابراین بهتر است برای هرگونه نیاز خاص، مستندات رسمی را مطالعه کنیم.
پس از اعمال این تغییرات، باید برنامهی خود را به دقت تست کنیم تا مطمئن شویم که انتقال آن به native Fetch API باعث ایجاد مشکل نشده است. به ویژه به مدیریت خطاها، timeoutها و هر منطق سفارشی مرتبط با درخواستهای HTTP باید توجه داشته باشیم.
بسیاری از توسعهدهندگان هنگام استفاده از Fetch در Node.js با خطاهایی مواجه میشوند. در ادمه قصد داریم تا برخی از رایجترین مشکلات و راهحلهای آنها را بررسی کنیم.
یکی از خطاهای متداول، ReferenceError: fetch is not defined
است که معمولاً در نسخههای قدیمیتر از نسخه ۱۸ رخ میدهد. Fetch در نسخه ۱۸ به عنوان یک ویژگی آزمایشی معرفی شد و در نسخه ۲۱ به یک قابلیت پایدار تبدیل گردید. برای حل این مشکل، باید به نسخه ۱۸ یا بالاتر بهروزرسانی کنیم.
همچنین، اگر از نسخههای آزمایشی (نسخه ۱۸ تا نسخه ۲۰) استفاده میکنیم، باید برنامهی خود را با flag آزمایشی اجرا نماییم:
node --experimental-fetch example.js
علاوه بر این، برای پروژههای قدیمی که امکان ارتقاء نسخه Node.js را ندارند، میتوانیم از کتابخانههایی مانند node-fetch برای پشتیبانی از Fetch استفاده کنیم.
خطای TypeError: Failed to fetch
معمولاً به دلیل مشکلات اتصال به شبکه، invalid endpointها یا محدودیتهای CORS رخ میدهد؛ یعنی زمانی که سروری که درخواست به آن ارسال میشود، هدرهای لازم برای مجاز دانستن درخواست از مبدا کلاینت را شامل نمیشود.
کارهایی که برای حل این مشکل باید انجام دهیم عبارتند از:
یکی دیگر از خطاهای رایج، TypeError: Cannot read property 'json' of undefined
است که معمولاً زمانی رخ میدهد که قصد داریم یک پاسخ JSON را parse کنیم اما سرور دادهها را به فرمت مورد انتظار ارسال نکرده است.
برای حل این مشکل باید:
blob
، bytes
یا text
ارسال کند. در این صورت، از متدهای مناسب برای خواندن دادهها استفاده کنیم. به عنوان مثال:fetch('https://api.example.com/data') .then(response => { if (!response.ok) { throw new Error(`Request failed with status: ${response.status}`); } return response.json(); // Use .blob(), .bytes(), or .text() if required }) .then(data => console.log(data)) .catch(error => console.error('Fetch error:', error));
Fetch در Node.js رفتاری شبیه به مرورگر دارد، اما برخلاف مرورگرها، به صورت خودکار کوکیها را مدیریت نمیکند؛ یعنی کوکیها به طور پیشفرض ذخیره یا ارسال نمیشوند، مگر اینکه آنها را به صورت دستی مدیریت کنیم.
اگر یک API برای احراز هویت به کوکیها وابسته باشد، باید آنها را به طور صریح در درخواستهای خود بگنجانیم یا از کتابخانههایی مانند fetch-cookie یا tough-cookie برای مدیریت کوکیها استفاده نماییم.
Fetch به صورت پیشفرض از timeout پشتیبانی نمیکند، بنابراین اگر سرور به طور نامحدود به درخواست پاسخ ندهد، درخواست ما ممکن است بیپایان در حالت انتظار باقی بماند. برای جلوگیری از این مشکل، میتوانیم از AbortController
برای تنظیم timeout استفاده کنیم. به عنوان مثال:
const controller = new AbortController(); const timeout = setTimeout(() => controller.abort(), 5000); // Timeout after 5 seconds fetch("http://example.com/api", { signal: controller.signal }) .then((response) => response.json()) .catch((err) => console.log("Fetch error:", err));
به این ترتیب، ما کنترل کاملی بر زمان انتظار برای پاسخ خواهیم داشت و میتوانیم در صورت لزوم درخواست را لغو نماییم.
پایدار شدن Fetch API جدید در Node.js، که با تلاشهای بیوقفه تیم اصلی Node.js و نقش کلیدی کتابخانهی HTTP پرکاربرد Undici ممکن شده است، گامی بزرگ برای توسعهدهندگان محسوب میشود.
این انتشار پایدار به این معنی است که اکنون میتوانیم بدون نگرانی از باگها یا مشکلات غیرمنتظره، از آن استفاده کنیم و تجربهای یکپارچه در ارسال درخواستهای HTTP در هر دو محیط مرورگر و سرور داشته باشیم.
۵۰ درصد تخفیف ویژه نوروز فرانت کست تا ۱۵ فروردین
کد تخفیف: spr
دیدگاهها: