<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"><channel><title>نوشته های garmabi.mohammad73</title><link>https://virgool.ir/feed/@garmabi.mohammad73</link><description>Senior Software Engineer با تمرکز بر Frontend، React و TypeScript. علاقه‌مند به Software Architecture، System Design، Open Source و ساخت ابزارهای ساده و مقیاس‌پذیر.</description><language>fa</language><pubDate>2026-11-10 15:28:34</pubDate><image><url>https://files.virgool.ir/upload/users/4943957/avatar/Litx8O.jpg</url><title>garmabi.mohammad73</title><link>https://virgool.ir/@garmabi.mohammad73</link></image><item><title>انتشار tanstack-fetch 2.1.0؛ لغو واقعی در retry، رفرش توکن امن‌تر و پارامترهای آرایه‌ای</title><link>https://virgool.ir/@garmabi.mohammad73/انتشار-tanstack-fetch-210-لغو-واقعی-در-retry-رفرش-توکن-امن-تر-و-پارامترهای-آرایه-ای-jqw8vosae0tn</link><description>نسخه اصلی این مطلب (انگلیسی): mohamadgarmabi.github.io/tanstack-fetch/blog/tanstack-fetch-2-1-0نسخه 2.1.0 کتابخانه tanstack-fetch یک نسخه با محوریت رفع باگ است و هیچ تغییر ناسازگاری (breaking change) ندارد. با ارتقا این‌ها را می‌گیرید:لغو یعنی لغو: اگر درخواست در حین تأخیرِ بین تلاش‌ها (retry delay) لغو شود، بلافاصله reject می‌شود.دونقطه در مسیر: مسیری مثل /models/gemini:generateContent دیگر پارامتری به اسم generateContent نمی‌خواهد.فقط یک رفرش برای هر چرخش توکن: یک 401 دیررس دیگر رفرش دوم را شروع نمی‌کند.مقادیر آرایه‌ای در query، گزینه‌های parseAs: &#39;arrayBuffer&#39; | &#39;stream&#39; و entry جدید tanstack-fetch/full.ارتقاnpm install tanstack-fetch@2.1.0باگ‌های رفع‌شدهلغو درخواست در حین تأخیر retryوقتی سرور Retry-After: 5 برمی‌گرداند (یا هر backoff دیگری)، درخواست HTTP حتی بعد از abort شدن signal کل زمان تأخیر را صبر می‌کرد. TanStack Query موقع unmount و تغییر queryKey درخواست را abort می‌کند، پس query چند ثانیه در حالت pending می‌ماند. حالا تأخیر به signal گوش می‌دهد و بلافاصله با AbortError رد می‌شود و تلاش بعدی هم ارسال نمی‌شود.useQuery({&#xA;  queryKey: [&#39;report&#39;, id],&#xA;  // unmount during a 5s Retry-After → rejects now, no second fetch&#xA;  queryFn: ({ signal }) =&gt; api.get(&#39;/reports/:id&#39;, { params: { id }, signal }),&#xA;})دونقطه داخل یک بخش از مسیرحالا :name فقط وقتی در ابتدای یک بخش از مسیر بیاید پارامتر مسیر حساب می‌شود؛ هم در زمان اجرا و هم در تایپ‌ها:await api.post(&#39;/v1/models/gemini:generateContent&#39;, { body }) // literal path, no params&#xA;await api.get(&#39;/users/:id&#39;, { params: { id: 1 } }) // → /users/1قبلاً فراخوانی اول با خطای missing path param &#34;generateContent&#34; شکست می‌خورد و TypeScript هم همین پارامتر را می‌خواست.رفرش توکن: بدون رفرش دوم برای 401 دیررسinterceptor از قبل یک رفرش در جریان را بین چند 401 موازی به اشتراک می‌گذاشت. اما درخواستی که با توکن قدیمی ارسال شده بود و 401اش بعد از تمام شدن رفرش می‌رسید، یک رفرش جدید شروع می‌کرد. با refresh tokenهای چرخشی (rotating)، این فراخوانی دوم می‌تواند نشستی را که اولی تازه ساخته باطل کند.حالا interceptor می‌داند هر درخواست بعد از کدام رفرش ارسال شده است. یک 401 دیررس فقط درخواست را با توکن جدید دوباره می‌فرستد:A ──(old token)──────────────────────► 401 → retry with new token ✓&#xA;B ──(old token)──► 401 → refresh() ✓ → retry ✓&#xA;                       refresh() called onceحالت برعکس هم درست شده است. درخواستی که به دلیل دیگری retry شده بود (مثلاً یک 502 از طریق retry-idempotent) و بعد 401 می‌گرفت، رفرش را رد می‌کرد، چون شرط بررسی attempt &gt; 0 بود. حالا هر درخواست حداکثر یک بار رفرش می‌کند و این به ازای هر فراخوانی ردیابی می‌شود، پس چرخه 401 → refresh → 401 همچنان نمی‌تواند حلقه بسازد.این قابلیت با یک context.meta.state جدید کار می‌کند؛ یک object ساده که بین همه تلاش‌های یک درخواست مشترک است. interceptorهای خودتان هم می‌توانند از آن برای flagهای مخصوص هر فراخوانی استفاده کنند.بدنه‌های stream دیگر retry نمی‌شوندبدنه از نوع ReadableStream در تلاش اول مصرف می‌شود، بنابراین retry قبلاً با یک TypeError گیج‌کننده درباره «locked stream» شکست می‌خورد. حالا درخواست‌هایی که بدنه stream دارند، به جای retry، خطای واقعی (مثلاً FetchError با وضعیت 503) را برمی‌گردانند.قابلیت‌های جدیدمقادیر آرایه‌ای در queryawait api.get(&#39;/users&#39;, { query: { ids: [1, 2], role: &#39;admin&#39; } })&#xA;// → /users?ids=1&amp;ids=2&amp;role=adminQueryParams حالا آرایه‌ای از string | number | boolean را می‌پذیرد و هر آیتم به صورت یک key=value جدا اضافه می‌شود. مقادیر null و undefined همچنان نادیده گرفته می‌شوند.گزینه parseAs: &#39;arrayBuffer&#39; | &#39;stream&#39;const bytes = await api.get&lt;ArrayBuffer&gt;(&#39;/files/:id&#39;, { params: { id }, parseAs: &#39;arrayBuffer&#39; })&#xA;const body = await api.get&lt;ReadableStream&lt;Uint8Array&gt;&gt;(&#39;/export&#39;, { parseAs: &#39;stream&#39; })حالت &#39;stream&#39; خودِ response.body را بدون خواندن برمی‌گرداند.entry جدید tanstack-fetch/fullکلاینت کامل HTTP (میان‌برهای option، تابع upload() و رویدادهای progress) قبلاً فقط زیر tanstack-fetch/sse بود که اگر از SSE استفاده نمی‌کنید عجیب به نظر می‌رسد. tanstack-fetch/full همان build با یک اسم واضح‌تر است و /sse هم مثل قبل کار می‌کند.import { createFetch } from &#39;tanstack-fetch/full&#39;تایپ‌های درست برای CommonJSحالا require(&#39;tanstack-fetch&#39;) به جای .d.ts نسخه ESM، به declarationهای .d.cts resolve می‌شود؛ بنابراین پروژه‌های TypeScript که با module: node16 یا nodenext و CJS کار می‌کنند تایپ‌های درست را می‌گیرند. CI هم حالا همه entryها را با publint و @arethetypeswrong/cli بررسی می‌کند و تست‌ها را روی Node 20، 22 و 24 اجرا می‌کند.حجم باندل (gzip)tanstack-fetch: از 3.57KB به 3.68KBtanstack-fetch/sse: از 6.33KB به 6.43KBtanstack-fetch/live: از 8.75KB به 8.84KBtanstack-fetch/plugins: از 1.13KB به 1.22KBیعنی حدود ۱۰۰ بایت برای همه این اصلاحات.نکتهtimeoutها و abortها با TimeoutError / AbortError خودِ پلتفرم reject می‌شوند، نه با FetchError. این همیشه همین‌طور بوده و حالا در README هم ذکر شده است. برای بررسی‌شان از isAbortError(error) استفاده کنید. (مستندات Errors)بیشتر بخوانید: آموزش refresh token · گزینه‌های درخواست · Changelog · مخزن GitHub</description><category>garmabi.mohammad73</category><author>garmabi.mohammad73</author><pubDate>Sat, 07 Nov 2026 12:50:32 +0330</pubDate></item><item><title>انتشار tanstack-fetch 2.0.0؛ هسته‌ای سبک‌تر، پشتیبانی از WebSocket و WebTransport و استریم Type-safe</title><link>https://virgool.ir/@garmabi.mohammad73/انتشار-tanstack-fetch-200-هسته-ای-سبک-تر-پشتیبانی-از-websocket-و-webtransport-و-استریم-type-safe-tds2zhii58hw</link><description>از یک HTTP Client سبک تا یک لایه ارتباطی یکپارچه برای اپلیکیشن‌های مدرن TypeScriptنسخه 2.0.0 کتابخانه tanstack-fetch منتشر شد.این نسخه صرفاً یک آپدیت معمولی نیست؛ در فاصله نسخه 1.7.1 تا 2.0.0، معماری کتابخانه تغییرات مهمی داشته و قابلیت‌های جدیدی برای ارتباطات Real-time و Streaming به آن اضافه شده است.مهم‌ترین تغییرات این نسخه عبارت‌اند از:کاهش حجم Core از 5.14KB به 3.57KB فشرده‌شده با gzipاضافه شدن WebSocketاضافه شدن WebTransportپشتیبانی از NDJSON Streamingیکپارچه‌سازی معماری HTTP و Live Transportبهبود DevTools برای بررسی ارتباطات زندهپشتیبانی کامل‌تر از Type-safe Params در APIهای زندهانتقال Authentication و Status Handling به Interceptorهادر این مقاله تغییرات نسخه‌های 1.8.0 و 2.0.0 را بررسی می‌کنیم.۱. پشتیبانی از WebSocketیکی از مهم‌ترین قابلیت‌های نسخه 1.8.0 اضافه شدن متد api.ws() است.در بسیاری از اپلیکیشن‌ها، HTTP به‌تنهایی کافی نیست. برای ساخت چت، داشبوردهای زنده، ابزارهای همکاری و سیستم‌های Real-time به ارتباط دوطرفه و پایدار نیاز داریم.حالا می‌توانیم از همان معماری کلاینت برای WebSocket استفاده کنیم:const socket = api.ws&lt;&#xA;  Message,&#xA;  { roomId: string },&#xA;  SendMessage&#xA;&gt;(&#39;/rooms/:roomId&#39;, {&#xA;  params: { roomId: &#39;general&#39; },&#xA;})&#xA;&#xA;socket.onMessage((message) =&gt; {&#xA;  console.log(message)&#xA;})&#xA;&#xA;socket.send({&#xA;  text: &#39;Hello!&#39;,&#xA;})قابلیت‌های مهم:اتصال مجدد خودکار با Exponential Backoffدریافت توکن جدید هنگام Reconnectصف ارسال پیام تا زمان برقراری اتصالپشتیبانی از AbortSignalپردازش پیام‌های JSONمدیریت Lifecycle اتصالهمچنین نحوه ارسال اطلاعات احراز هویت با توجه به محدودیت‌های محیط اجرا طراحی شده است؛ در مرورگر، توکن به‌صورت پیش‌فرض داخل URL قرار نمی‌گیرد.۲. پشتیبانی از WebTransportدر کنار WebSocket، قابلیت WebTransport نیز اضافه شده است.WebTransport مبتنی بر HTTP/3 است و امکان استفاده از Datagram و سایر الگوهای ارتباطی را فراهم می‌کند.const connection = api.transport(&#39;/live&#39;, {&#xA;  // WebTransport options&#xA;})این قابلیت از تنظیماتی مانند serverCertificateHashes و پیاده‌سازی سفارشی از طریق createWebTransport پشتیبانی می‌کند.۳. پشتیبانی از NDJSON Streamingقابلیت مهم دیگر api.stream() است.در پاسخ‌های JSON معمولی، معمولاً باید منتظر دریافت کل پاسخ بمانیم؛ اما با NDJSON سرور می‌تواند داده‌ها را به‌صورت تدریجی ارسال کند.const stream = api.stream&lt;Message&gt;(&#39;/events&#39;, {&#xA;  method: &#39;POST&#39;,&#xA;  body: {&#xA;    prompt: &#39;Hello&#39;,&#xA;  },&#xA;})&#xA;&#xA;for await (const message of stream) {&#xA;  console.log(message)&#xA;}این قابلیت شامل موارد زیر است:پشتیبانی از متدهای مختلف HTTPپشتیبانی از Request Bodyاستفاده از Interceptorهای مشترکRetry و مدیریت خطاRefresh Tokenلغو درخواست با AbortSignalسازگاری با streamedQuery در TanStack Query۴. معماری یکپارچه برای ارتباطات زندهدر نسخه 1.8.0 یک Entry Point جدید معرفی شد:import { createFetch } from &#39;tanstack-fetch/live&#39;این ماژول شامل متدهای زیر است:api.sse()api.ws()api.transport()api.stream()به این ترتیب، برای استفاده از HTTP، SSE، WebSocket و NDJSON نیازی به پیاده‌سازی چند معماری کاملاً جداگانه نداریم.همچنین Hookهای جدیدی برای Interceptorها اضافه شده‌اند:onLiveOpenonLiveMessageonLiveCloseاین Hookها امکان مشاهده وضعیت اتصال و پیام‌های ورودی و خروجی را فراهم می‌کنند.۵. بهبود DevToolsدر نسخه 1.8.0 دو تب جدید به DevTools اضافه شده است:WS برای WebSocket و WebTransportStream برای NDJSONهمچنین برای هر Connection یک Message Log در نظر گرفته شده تا بررسی و Debug کردن ارتباطات زنده ساده‌تر شود.CLI نیز بهبود پیدا کرده است و دستور doctor می‌تواند استفاده مستقیم از new WebSocket و new EventSource را شناسایی کند.۶. کاهش حجم Core در نسخه 2.0.0یکی از مهم‌ترین تغییرات معماری نسخه 2.0.0 کاهش حجم Core است.&lt;escape&gt;5.14KB → 3.57KB gzip&lt;/escape&gt;یعنی تقریباً ۳۰ درصد کاهش حجم.در این نسخه، Core فقط روی مسئولیت‌های اصلی HTTP متمرکز شده است:مدیریت Base URLHeadersTimeoutInterceptorsFetch implementationCredentialsRetry configurationقابلیت‌هایی مانند Authentication و Status Handling از Core خارج شده‌اند و از طریق Interceptorهای مستقل استفاده می‌شوند.برای مثال:import { createFetch } from &#39;tanstack-fetch&#39;&#xA;&#xA;import {&#xA;  createAuthInterceptor,&#xA;  createStatusInterceptor,&#xA;} from &#39;tanstack-fetch/plugins&#39;&#xA;&#xA;const api = createFetch({&#xA;  baseUrl: &#39;/api&#39;,&#xA;&#xA;  interceptors: [&#xA;    createAuthInterceptor({&#xA;      getToken,&#xA;    }),&#xA;&#xA;    createStatusInterceptor({&#xA;      401: handleUnauthorized,&#xA;      &#39;5xx&#39;: handleServerError,&#xA;    }),&#xA;  ],&#xA;})این تغییر باعث می‌شود قابلیت‌های اختیاری به‌جای قرار گرفتن در هسته اصلی، به‌صورت مستقل و ترکیب‌پذیر استفاده شوند.نکته مهم: این تغییر به معنای حذف کامل قابلیت Upload نیست. ماژول‌های tanstack-fetch/sse، /live، /trpc و Framework Providerها همچنان قابلیت‌های قبلی خود را حفظ کرده‌اند.۷. Type-safe Params برای APIهای Liveدر نسخه 2.0.0، تایپ پارامترهای مسیر به APIهای زنده نیز گسترش پیدا کرده است.پیش از این، HTTP از قابلیت Type-safe Params برخوردار بود، اما APIهای Live از همین سطح استنتاج تایپ پشتیبانی نمی‌کردند.حالا می‌توانیم بنویسیم:const socket = api.ws&lt;&#xA;  Message,&#xA;  { roomId: number }&#xA;&gt;(&#xA;  &#39;/rooms/:roomId&#39;,&#xA;  {&#xA;    params: {&#xA;      roomId: 42,&#xA;    },&#xA;  },&#xA;)این قابلیت در موارد زیر در دسترس است:api.sse()api.ws()api.transport()api.stream()useSse()useWs()همچنین برای React، Vue، Solid، Svelte و Angular در نظر گرفته شده است.ترتیب Genericها نیز یکپارچه‌تر شده است:// WebSocket&#xA;&lt;Message, Params, Send&gt;&#xA;&#xA;// SSE / NDJSON&#xA;&lt;Message, Params&gt;۸. اصلاحات مهم در Streamingدر نسخه 2.0.0 چند مشکل مهم در چرخه اجرای NDJSON نیز برطرف شده است:ارسال صحیح ReadableStream در Node.js با duplex: &#39;half&#39;اجرای onRequestError هنگام خطاهای شبکهلغو فوری Stream هنگام Abort شدن در زمان Retry Delay۹. مهاجرت از نسخه 1.7.1 به 2.0.0برای ارتقا:npm install tanstack-fetch@2.0.0چند نکته مهم در Migration وجود دارد:۱. تنظیمات getToken و Status Handlerها را به Interceptor منتقل کنید.۲. اگر از upload() در Core استفاده می‌کردید، Entry Point مربوط به tanstack-fetch/sse را بررسی کنید.۳. ترتیب Genericهای WebSocket را به ساختار جدید تغییر دهید.۴. اگر از api.stream() استفاده می‌کنید، حتماً مدیریت AbortSignal و Retry را بررسی کنید.جمع‌بندیهدف tanstack-fetch ساخت یک Axios دیگر نیست.ایده اصلی این پروژه فراهم کردن یک لایه ارتباطی سبک، Type-safe و قابل ترکیب است که در کنار TanStack Query به‌خوبی کار کند.HTTP، SSE، WebSocket، WebTransport و NDJSON روش‌های مختلفی برای ارتباط با سرور هستند؛ اما نباید برای هرکدام مجبور باشیم معماری مستقلی برای Authentication، مدیریت خطا، Retry و Observability بسازیم.در نسخه 2.0.0، Core کوچک‌تر شده، معماری ماژولارتر شده و پشتیبانی از ارتباطات زنده توسعه پیدا کرده است.نصب:npm install tanstack-fetchلینک‌های پروژه:مستندات: https://mohamadgarmabi.github.io/tanstack-fetch/GitHub: https://github.com/mohamadgarmabi/tanstack-fetchnpm: https://www.npmjs.com/package/tanstack-fetchRelease Notes: https://mohamadgarmabi.github.io/tanstack-fetch/blog/tanstack-fetch-2-0-0این پروژه یک کتابخانه مستقل و Open Source است و محصول رسمی TanStack نیست.خوشحال می‌شوم تجربه و نظرتان را درباره معماری جدید و قابلیت‌های نسخه 2.0.0 بدانم.</description><category>garmabi.mohammad73</category><author>garmabi.mohammad73</author><pubDate>Sat, 31 Oct 2026 12:00:06 +0330</pubDate></item><item><title>چطور در React درخواستهای API را کنترل کنیم؟ آموزش TanStack Pacer در کنار TanStack Query</title><link>https://virgool.ir/@garmabi.mohammad73/httpsmohamadgarmabigithubiotanstack-fetchblogtanstack-pacer-tanstack-query-tanstack-fetch-bycci5t58cop</link><description>اگر اپلیکیشن React شما درخواستهای API زیادی ارسال میکند، همیشه مشکل از HTTP Client نیست. گاهی مشکل اصلی این است که درخواستها چه زمانی و با چه فاصلهای اجرا میشوند.چند مثال آشنا:با هر بار تایپ کاربر در Search یک درخواست ارسال میشود.هنگام ویرایش متن، چند درخواست Autosave همزمان اجرا میشوند.هنگام اسکرول، محاسبات و بررسیهای تکراری انجام میشوند.با انتخاب تعداد زیادی فایل، همه آپلودها همزمان شروع میشوند.هر رویداد Analytics یک درخواست مستقل ایجاد میکند.در این مقاله، شش الگوی کاربردی را با استفاده از سه کتابخانه بررسی میکنیم:TanStack Pacer: کنترل زمان اجرا و تعداد دفعات اجرای عملیاتTanStack Query: مدیریت Server State، کش و چرخه عمر درخواستهاtanstack-fetch: مدیریت درخواستهای HTTP و خطاهای تایپشدهمن Mohammad Garmabi، سازنده کتابخانه tanstack-fetch هستم. این کتابخانه یک پروژه مستقل جامعه توسعهدهندگان است و پروژه رسمی تیم TanStack نیست.۱. نصب کتابخانهها و ساخت HTTP Clientابتدا پکیجهای موردنیاز را نصب میکنیم:npm install @tanstack/react-pacer @tanstack/react-query tanstack-fetchسپس یک Client مشترک میسازیم:// src/lib/api.ts&#xA;import { createFetch } from &#39;tanstack-fetch&#39;&#xA;&#xA;export const api = createFetch({&#xA;  baseUrl: import.meta.env.VITE_API_URL,&#xA;  timeoutMs: 10_000,&#xA;})در این معماری، هر کتابخانه مسئولیت مشخصی دارد. Pacer زمان اجرای عملیات را کنترل میکند، Query مسئول کش و وضعیت دادههاست و tanstack-fetch ارتباط HTTP را مدیریت میکند.نمونههای این مقاله بر اساس API نسخههای موردنظر نوشته شدهاند. پیش از استفاده در پروژه واقعی، امضای متدها را با نسخه نصبشده و مستندات فعلی بررسی کنید.۲. پیادهسازی Search با Debounceفرض کنید کاربر عبارت tanstack را جستوجو میکند. اگر با هر کاراکتر یک درخواست بفرستیم، تعداد زیادی درخواست غیرضروری ایجاد میشود.با Debounce میتوانیم صبر کنیم تا کاربر برای مدت کوتاهی تایپ را متوقف کند.import { useState } from &#39;react&#39;&#xA;import { useDebouncedValue } from &#39;@tanstack/react-pacer&#39;&#xA;import { keepPreviousData, useQuery } from &#39;@tanstack/react-query&#39;&#xA;import { api } from &#39;~/lib/api&#39;&#xA;&#xA;type User = {&#xA;  id: number&#xA;  name: string&#xA;  email: string&#xA;}&#xA;&#xA;export function SearchUsers() {&#xA;  const [search, setSearch] = useState(&#39;&#39;)&#xA;&#xA;  const [debouncedSearch] = useDebouncedValue(search, {&#xA;    wait: 300,&#xA;  })&#xA;&#xA;  const query = useQuery({&#xA;    queryKey: [&#39;users&#39;, &#39;search&#39;, debouncedSearch],&#xA;    queryFn: ({ signal }) =&gt;&#xA;      api.get&lt;User[]&gt;(&#39;/users&#39;, {&#xA;        query: { q: debouncedSearch },&#xA;        signal,&#xA;      }),&#xA;    enabled: debouncedSearch.trim().length &gt;= 2,&#xA;    placeholderData: keepPreviousData,&#xA;    staleTime: 30_000,&#xA;  })&#xA;&#xA;  return (&#xA;    &lt;&gt;&#xA;      &lt;input&#xA;        value={search}&#xA;        ={(event) =&gt; setSearch(event.target.value)}&#xA;        placeholder=&#34;جستوجوی کاربران&#34;&#xA;      /&gt;&#xA;&#xA;      {query.isFetching &amp;&amp; &lt;span&gt;در حال جستوجو...&lt;/span&gt;}&#xA;&#xA;      {query.data?.map((user) =&gt; (&#xA;        &lt;div key={user.id}&gt;{user.name}&lt;/div&gt;&#xA;      ))}&#xA;    &lt;/&gt;&#xA;  )&#xA;}چند نکته مهم در این پیادهسازی وجود دارد:مقدار Input بلافاصله تغییر میکند و تایپ کاربر کند نمیشود.فقط مقدار debouncedSearch وارد queryKey میشود.هر عبارت جستوجو میتواند کش مستقل خودش را داشته باشد.با keepPreviousData، نتایج قبلی هنگام بارگذاری نتایج جدید باقی میمانند.با مصرف signal در queryFn، درخواستهای قبلی امکان لغو شدن دارند.نکته: بهتر است خود queryFn را با یک تابع Debounce معمولی Wrap نکنید. مقدار ورودی به queryKey را Debounce کنید تا قرارداد Promise مورد انتظار TanStack Query حفظ شود.۳. Autosave با قابلیت لغو درخواستدر قابلیت Autosave، هدف این است که بعد از توقف تایپ، آخرین نسخه متن ذخیره شود. اما فقط تأخیر انداختن درخواست کافی نیست؛ باید به درخواستهایی که قبلاً شروع شدهاند هم توجه کنیم.یک جریان مناسب شامل این مراحل است:۱. بعد از توقف تایپ، عملیات ذخیره اجرا شود.۲. درخواست PATCH همراه با AbortSignal ارسال شود.۳. در صورت کنار گذاشتن تغییرات، عملیات در انتظار لغو شود و درخواست در حال اجرا نیز در صورت امکان Abort شود.۴. پس از دریافت پاسخ موفق، کش TanStack Query بهروز شود.برای مدیریت زمانبندی عملیات غیرهمزمان میتوان از useAsyncDebouncer در TanStack Pacer استفاده کرد و AbortSignal را به درخواست tanstack-fetch داد.با این حال، یک نکته مهم را نباید فراموش کنیم: لغو درخواست در مرورگر لزوماً به معنی لغو پردازش آن در سرور نیست. اگر صحت داده اهمیت زیادی دارد، بهتر است از مکانیزمهایی مانند Version Check یا Optimistic Concurrency در سمت سرور هم استفاده کنیم.۴. کنترل Infinite Scroll با Throttleرویدادهای Scroll میتوانند در هر ثانیه چندین بار اجرا شوند. اگر در هر رویداد موقعیت اسکرول را بررسی کنیم، محاسبات تکراری خواهیم داشت.با Throttle میتوانیم تعداد دفعات اجرای Callback را محدود کنیم:const  = useThrottledCallback(&#xA;  (element: HTMLElement) =&gt; {&#xA;    const nearBottom =&#xA;      element.scrollTop + element.clientHeight &gt;&#xA;      element.scrollHeight - 600&#xA;&#xA;    if (nearBottom &amp;&amp; hasNextPage &amp;&amp; !isFetchingNextPage) {&#xA;      void fetchNextPage()&#xA;    }&#xA;  },&#xA;  { wait: 200 },&#xA;)در این مثال، تابع در هر بازه زمانی حداکثر با محدودیت تعریفشده اجرا میشود. TanStack Query همچنان مسئول مدیریت صفحهها و وضعیت درخواست است.برای بعضی رابطهای کاربری، استفاده از IntersectionObserver و یک عنصر مشخص در انتهای لیست نیز انتخاب مناسبی است.۵. Rate Limiting برای جلوگیری از درخواستهای بیشازحدفرض کنید یک API موقعیتیابی، حداکثر پنج درخواست در ثانیه را مجاز میداند. میتوانیم پیش از ارسال درخواست، تعداد عملیات را در سمت کلاینت کنترل کنیم.const limiter = useAsyncRateLimiter(&#xA;  (address: string) =&gt;&#xA;    api.get&lt;Place[]&gt;(&#39;/geocode&#39;, {&#xA;      query: { address },&#xA;    }),&#xA;  {&#xA;    limit: 5,&#xA;    window: 1_000,&#xA;    windowType: &#39;sliding&#39;,&#xA;    onReject: (_args, rateLimiter) =&gt; {&#xA;      const seconds = Math.ceil(&#xA;        rateLimiter.getMsUntilNextWindow() / 1000,&#xA;      )&#xA;&#xA;      toast(`لطفاً ${seconds} ثانیه دیگر تلاش کنید`)&#xA;    },&#xA;  },&#xA;)این روش به کاهش درخواستهای غیرضروری و ارائه بازخورد سریع به کاربر کمک میکند.اما Rate Limiting سمت کلاینت، محدودیت امنیتی یا سراسری نیست. کاربر میتواند از تب یا دستگاه دیگری درخواست بفرستد. بنابراین سرور باید محدودیت واقعی را اعمال کند.برای پاسخ HTTP با کد 429 Too Many Requests نیز میتوان سیاست Retry را در لایه Transport تعریف کرد و در صورت پشتیبانی نسخه نصبشده، مقدار Retry-After را در نظر گرفت.۶. مدیریت آپلودهای همزمان با Queueفرض کنید کاربر ۲۰۰ تصویر را برای آپلود انتخاب کرده است. شروع همزمان تمام درخواستها میتواند فشار زیادی به مرورگر، شبکه و سرور وارد کند.با Queue میتوان تعداد درخواستهای فعال را محدود کرد:const queue = useAsyncQueuer(&#xA;  (job: UploadJob) =&gt;&#xA;    api.upload&lt;Photo&gt;(&#39;/photos&#39;, {&#xA;      file: job.file,&#xA;      fields: { albumId: job.albumId },&#xA;    }),&#xA;  {&#xA;    concurrency: 3,&#xA;    wait: 100,&#xA;    onSuccess: (_photo, job) =&gt;&#xA;      queryClient.invalidateQueries({&#xA;        queryKey: [&#39;albums&#39;, job.albumId, &#39;photos&#39;],&#xA;      }),&#xA;    : (error, job) =&gt;&#xA;      console.error(`Upload failed: ${job.file.name}`, error),&#xA;  },&#xA;)در این مثال، حداکثر سه آپلود بهصورت همزمان فعال هستند و سایر فایلها در صف باقی میمانند.این ساختار میتواند پایهای برای امکانات زیر باشد:نمایش درصد پیشرفت آپلودهاتوقف و ادامه صفاولویتبندی فایلهامدیریت خطای هر فایل بهصورت مستقلبه محدود کردن حجم کار همزمان، Backpressure گفته میشود؛ یعنی بهجای شروع تمام عملیات، نرخ ورود کار به سیستم را کنترل میکنیم.۷. Batch کردن رویدادهای Analyticsفرض کنید در یک صفحه تعداد زیادی رویداد ثبت میشود. ارسال یک درخواست HTTP برای هر رویداد، سربار غیرضروری ایجاد میکند.با Batcher میتوان چند رویداد را در یک درخواست ارسال کرد:const batcher = useAsyncBatcher(&#xA;  (events: TrackEvent[]) =&gt;&#xA;    api.post(&#39;/events&#39;, { body: { events } }),&#xA;  {&#xA;    maxSize: 20,&#xA;    wait: 2_000,&#xA;    : (error, batch) =&gt;&#xA;      console.warn(`Batch of ${batch.length} events failed`, error),&#xA;  },&#xA;)&#xA;&#xA;batcher.addItem({&#xA;  name: &#39;button_clicked&#39;,&#xA;  at: Date.now(),&#xA;  props: { page: &#39;/dashboard&#39; },&#xA;})در این مثال، رویدادها با رسیدن به سقف تعیینشده یا بر اساس زمانبندی Batcher ارسال میشوند.برای استفاده در محیط Production باید درباره Retry، جلوگیری از ثبت تکراری رویدادها و ارسال رویدادهای باقیمانده هنگام مخفی شدن صفحه نیز تصمیم بگیریم.۸. یک اشتباه رایج: Retry در چند لایهTanStack Query، Pacer و HTTP Client ممکن است هرکدام امکان Retry داشته باشند.اگر هر سه لایه بهصورت مستقل یک عملیات را Retry کنند، یک درخواست ناموفق میتواند به تعداد زیادی درخواست تبدیل شود.بهتر است مسئولیتها را تفکیک کنیم:لایه HTTP: رفتارهای وابسته به وضعیت پاسخ مانند 429 و Retry-AfterTanStack Query: سیاست Retry برای QueryهاTanStack Pacer: Retry برای کارهای صفبندیشده یا Batchهایی که مسئولیت آنها در جای دیگری مدیریت نمیشودبرای مثال، میتوان در Query از Retry کردن بیشتر خطاهای سمت کلاینت جلوگیری کرد:import { isFetchError } from &#39;tanstack-fetch&#39;&#xA;&#xA;retry: (count, error) =&gt;&#xA;  !(isFetchError(error) &amp;&amp; error.status &lt; 500) &amp;&amp;&#xA;  count &lt; 3این صرفاً یک سیاست نمونه است و باید با رفتار واقعی API شما تطبیق داده شود.۹. کدام ابزار را انتخاب کنیم؟نیازابزارجستوجو و فیلترهاuseDebouncedValueAutosaveuseAsyncDebouncerرویدادهای Scroll و ResizeuseThrottledCallbackمحدودیت تعداد درخواستهاuseAsyncRateLimiterآپلود گروهیuseAsyncQueuerتجمیع رویدادهای AnalyticsuseAsyncBatcherبرای جزئیات API و گزینههای هر ابزار، مستندات TanStack Pacer را بررسی کنید.جمعبندیبرای ساخت یک لایه ارتباطی قابل نگهداری در React، بهتر است مسئولیتها را از هم جدا کنیم:TanStack Pacer مشخص میکند عملیات چه زمانی اجرا شود.TanStack Query وضعیت دادههای سمت سرور، کش و چرخه عمر Queryها را مدیریت میکند.tanstack-fetch مسئول ارسال درخواستهای HTTP و مدیریت رفتارهای لایه Transport است.این تفکیک باعث میشود کنترل درخواستها، کش، لغو عملیات و مدیریت خطاها سادهتر و قابلفهمتر باشد.لینکهای بیشترمستندات TanStack Pacerمستندات TanStack Queryمستندات tanstack-fetchنویسنده: Mohammad Garmabi، سازنده tanstack-fetch؛ یک کتابخانه مستقل HTTP Client برای اکوسیستم فرانتاند.</description><category>garmabi.mohammad73</category><author>garmabi.mohammad73</author><pubDate>Wed, 28 Oct 2026 10:55:29 +0330</pubDate></item><item><title>بازطراحی سایت tanstack-fetch؛ تجربه‌ای بهتر برای توسعه‌دهنده‌ها 🚀</title><link>https://virgool.ir/@garmabi.mohammad73/httpsmohamadgarmabigithubiotanstack-fetch-dxorruet6wdz</link><description>سایت پروژه متن‌باز tanstack-fetch رو بازطراحی کردم.هدفم این بود که معرفی پروژه و دسترسی به مستندات، تجربه ساده‌تر و مرتب‌تری داشته باشه.ایده اصلی این کتابخانه، تکمیل تجربه کار با TanStack Query هست؛ به این شکل که مدیریت Server State در TanStack Query باقی می‌مونه و مسئولیت ارتباط HTTP و مدیریت درخواست‌ها به tanstack-fetch سپرده می‌شه.اگر با React، TypeScript و TanStack Query کار می‌کنید، خوشحال می‌شم نگاهی به نسخه جدید سایت بندازید و نظرتون رو بگید.🔗 مشاهده سایت:https://mohamadgarmabi.github.io/tanstack-fetch/بازخوردها و مشارکت شما می‌تونه به بهتر شدن این پروژه متن‌باز کمک کنه.</description><category>garmabi.mohammad73</category><author>garmabi.mohammad73</author><pubDate>Wed, 21 Oct 2026 11:55:31 +0330</pubDate></item><item><title>انتشار tanstack-fetch 1.7.1؛ رفع باگ‌های مهم در URL، Timeout، بدنه‌های باینری و مدیریت خطا</title><link>https://virgool.ir/@garmabi.mohammad73/انتشار-tanstack-fetch-171-رفع-باگ-های-مهم-در-url-timeout-بدنه-های-باینری-و-مدیریت-خطا-skzwtpwk4gkq</link><description>در نسخه‌ی 1.7.1 از tanstack-fetch روی چند مشکل در مسیر اصلی ارسال درخواست تمرکز کرده‌ام؛ مواردی که شاید در مثال‌های ساده دیده نشوند، اما در پروژه‌های واقعی، SSR و محیط‌های Node.js می‌توانند دردسرساز شوند.این نسخه API جدیدی اضافه نمی‌کند. هدف آن قابل‌پیش‌بینی‌تر کردن رفتار درخواست‌ها و اصلاح چند edge case مهم است.برای نصب:npm install tanstack-fetch@1.7.1۱. تشخیص اشتباه پارامترهای مسیر در URL اصلاح شدقبلاً matcher پارامترهای مسیر، کل URL را بررسی می‌کرد. در نتیجه ممکن بود :8080 در آدرس http://localhost:8080 یا مقداری مثل 10:30 در query string به‌اشتباه پارامتر مسیر در نظر گرفته شود.در نسخه‌ی جدید فقط pathname برای پیدا کردن پارامترهایی مثل :id یا {id} بررسی می‌شود و scheme، host، port، query string و hash دست‌نخورده باقی می‌مانند.await api.get(&#xA;  &#39;http://localhost:8080/users/:id?at=10:30&#39;,&#xA;  { params: { id: &#39;1&#39; } },&#xA;)&#xA;// → http://localhost:8080/users/1?at=10:30این اصلاح به‌خصوص در سناریوهای SSR که درخواست به یک origin محلی ارسال می‌شود کاربرد دارد.۲. Timeout دیگر با خطای معمول شبکه اشتباه گرفته نمی‌شوددر نسخه‌های قبلی، تمام شدن زمان درخواست با NETWORK_ERROR گزارش می‌شد. در نتیجه تشخیص timeout از قطع شدن اتصال سخت بود و ممکن بود منطق retry سفارشی آن را مثل یک خطای شبکه در نظر بگیرد.حالا timeout با TimeoutError رد می‌شود و تابع isAbortError() آن را تشخیص می‌دهد:import { isAbortError, isFetchError } from &#39;tanstack-fetch&#39;&#xA;&#xA;try {&#xA;  await api.get(&#39;/report&#39;, { timeoutMs: 5_000 })&#xA;} catch (error) {&#xA;  if (isAbortError(error)) {&#xA;    if ((error as Error).name === &#39;TimeoutError&#39;) {&#xA;      // نمایش پیام اختصاصی برای تمام شدن زمان درخواست&#xA;    }&#xA;  } else if (isFetchError(error)) {&#xA;    // مدیریت خطاهای HTTP یا شبکه&#xA;  }&#xA;}به این ترتیب می‌توان timeout را از cancellation ناشی از کاربر یا TanStack Query جدا کرد.۳. تایمرهای Timeout پس از پایان درخواست پاک می‌شوندپیش از این، ممکن بود تایمر timeout حتی بعد از موفق شدن درخواست تا پایان مدت تعیین‌شده فعال بماند. در Node.js، SSR و محیط‌های serverless این موضوع می‌توانست باعث زنده ماندن غیرضروری process یا انباشته شدن تایمرها زیر بار شود.در نسخه‌ی 1.7.1 تایمر پس از پایان هر تلاش پاک می‌شود و در Node.js نیز به‌تنهایی مانع پایان process نخواهد شد.۴. بدنه‌های باینری و Stream به‌صورت صحیح ارسال می‌شوندقبلاً ممکن بود Uint8Array به‌جای ارسال بایت‌های خام، به JSON تبدیل شود؛ چیزی شبیه:{&#34;0&#34;:1,&#34;1&#34;:2,&#34;2&#34;:3}اکنون ArrayBuffer، typed arrayها، DataView و ReadableStream به شکل مناسب خود ارسال می‌شوند.برای stream body نیز گزینه‌ی duplex: &#39;half&#39; تنظیم می‌شود تا با Fetch در Node.js سازگار باشد. آبجکت‌های معمولی JavaScript همچنان به JSON تبدیل می‌شوند.۵. خطاهای HTTP وضعیت واقعی پاسخ را حفظ می‌کنندگاهی یک gateway یا proxy پاسخ 502 را همراه با صفحه‌ی HTML برمی‌گرداند، حتی اگر content-type آن JSON باشد.قبلاً خطای parse می‌توانست پیش از اجرای interceptorها رخ دهد؛ در نتیجه status واقعی HTTP در دسترس منطق مدیریت خطا قرار نمی‌گرفت.در نسخه‌ی جدید، پاسخ non-2xx با JSON نامعتبر همچنان status واقعی خود را حفظ می‌کند. کد خطا HTTP_ERROR است، متن خام پاسخ در error.body قرار می‌گیرد و interceptorهای onResponseError اجرا می‌شوند.try {&#xA;  await api.get(&#39;/gateway&#39;)&#xA;} catch (error) {&#xA;  if (isFetchError(error)) {&#xA;    error.status // 502&#xA;    error.code   // &#39;HTTP_ERROR&#39;&#xA;    error.body   // متن خام پاسخ gateway&#xA;  }&#xA;}خطای PARSE_ERROR اکنون برای JSON نامعتبر در پاسخ‌های موفق 2xx استفاده می‌شود.نکته مهم هنگام ارتقاتنها تغییر رفتاری مهم این نسخه مربوط به timeout است:اگر با error.code === &#39;NETWORK_ERROR&#39; timeout را تشخیص می‌دادید، منطق خود را به isAbortError(error) تغییر دهید و در صورت نیاز error.name === &#39;TimeoutError&#39; را بررسی کنید.timeoutها دیگر از onRequestError عبور نمی‌کنند. اگر retry سفارشی شما فقط در این interceptor پیاده‌سازی شده، باید محل اجرای retry را بازبینی کنید.می‌توانید TimeoutError را در محل فراخوانی مدیریت کنید یا از قابلیت retry در TanStack Query استفاده کنید.جمع‌بندینسخه‌ی 1.7.1 بیشتر از آن‌که روی قابلیت‌های جدید تمرکز کند، روی رفتار درست در شرایط واقعی تمرکز دارد: URLهای دارای port و query، timeout، پاک‌سازی منابع، ارسال داده‌های باینری و حفظ اطلاعات خطاهای HTTP.نصب نسخه‌ی جدید:npm install tanstack-fetch@1.7.1مشاهده توضیحات کامل انتشار:https://mohamadgarmabi.github.io/tanstack-fetch/blog/tanstack-fetch-1-7-1tanstack-fetch یک پروژه‌ی مستقل برای مدیریت لایه‌ی HTTP در کنار ابزارهایی مانند TanStack Query است و پکیج رسمی TanStack نیست.— محمد گرمابی</description><category>garmabi.mohammad73</category><author>garmabi.mohammad73</author><pubDate>Sun, 18 Oct 2026 11:21:08 +0330</pubDate></item><item><title>TanStack Start 1.0 + tanstack-fetch: 6 Real-World Data Fetching Patterns</title><link>https://virgool.ir/@garmabi.mohammad73/httpsmediumcomsadboy7mtanstack-start-1-0-tanstack-fetch-6-real-world-data-fetching-patterns-940db6a89be6-syzfki4ua24c</link><description>TanStack Start is officially stable. If you have been waiting for the 1.0 release before building a production application, now is a good time to explore the framework.But getting a full-stack React application running is only the first step. The more interesting engineering decisions come later:Where should an API request run: in a route loader, on the server, or in the browser?How do you avoid fetching the same data twice?How should server-side rendering handle cookies and authorization?How do mutations update the existing Query cache?What happens when an API returns an HTTP error?How can a page combine REST data with live server-sent events?In this tutorial, we&#39;ll explore these scenarios using TanStack Start, TanStack Router, TanStack Query, and tanstack-fetch.I&#39;m the creator of tanstack-fetch, an HTTP client designed to work with TanStack Query&#39;s server-state model. It is an independent community package, not an official TanStack project.1. Start with the right mental modelThese libraries solve different problems:TanStack Start provides the full-stack application framework, including server rendering and server functions.TanStack Router coordinates routes, navigation, search parameters, and loaders.TanStack Query manages server-state caching, freshness, mutations, and background updates.tanstack-fetch handles HTTP requests, typed responses and errors, and reusable request behavior.The important distinction is that an HTTP client and a server-state manager should not compete for the same responsibilities.TanStack Query owns the cache. The HTTP client executes requests.Install the core dependencies:npm install tanstack-fetch @tanstack/react-queryFollow the current TanStack Start and Router documentation for the framework-specific setup and SSR integration:TanStack Start documentationtanstack-fetch documentation2. Pattern one: Fetch data directlyNot every request needs a complicated abstraction.For a simple client-side request, create a reusable API client:// src/lib/api.ts&#xA;import { createFetch } from &#39;tanstack-fetch&#39;&#xA;&#xA;export const api = createFetch({&#xA;  baseUrl: &#39;/api&#39;,&#xA;})Then use it in a component or a query function:import { useQuery } from &#39;@tanstack/react-query&#39;&#xA;import { api } from &#39;~/lib/api&#39;&#xA;&#xA;type User = {&#xA;  id: string&#xA;  name: string&#xA;}&#xA;&#xA;export function Users() {&#xA;  const usersQuery = useQuery({&#xA;    queryKey: [&#39;users&#39;],&#xA;    queryFn: ({ signal }) =&gt;&#xA;      api.get&lt;User[]&gt;(&#39;/users&#39;, { signal }),&#xA;  })&#xA;&#xA;  if (usersQuery.isPending) {&#xA;    return &lt;p&gt;Loading users...&lt;/p&gt;&#xA;  }&#xA;&#xA;  if (usersQuery.isError) {&#xA;    return &lt;p&gt;Could not load users.&lt;/p&gt;&#xA;  }&#xA;&#xA;  return (&#xA;    &lt;ul&gt;&#xA;      {usersQuery.data.map((user) =&gt; (&#xA;        &lt;li key={user.id}&gt;{user.name}&lt;/li&gt;&#xA;      ))}&#xA;    &lt;/ul&gt;&#xA;  )&#xA;}Three details matter here:The response is typed as User[].The query function passes Query&#39;s AbortSignal to the HTTP client.HTTP failures should reject the query so Query can represent them through its error state.Passing the signal also allows an obsolete request to be cancelled when appropriate.When to use this pattern: Client-side widgets, independent requests, and pages that do not need route-level preloading.3. Pattern two: Share route loaders and Query cacheThis is where TanStack Router and TanStack Query become particularly useful together.A common implementation fetches data in a loader, then makes another request when the component mounts. Another implementation stores loader data separately from Query&#39;s cache.Both approaches can be valid, but they introduce extra synchronization work when the same resource is used across routes or components.Instead, define reusable query options:// src/queries/users.ts&#xA;import { queryOptions } from &#39;@tanstack/react-query&#39;&#xA;import { api } from &#39;~/lib/api&#39;&#xA;&#xA;export const usersQueryOptions = queryOptions({&#xA;  queryKey: [&#39;users&#39;],&#xA;  queryFn: ({ signal }) =&gt;&#xA;    api.get&lt;User[]&gt;(&#39;/users&#39;, { signal }),&#xA;  staleTime: 30_000,&#xA;})Use the same options in the route loader and component:import { createFileRoute } from &#39;@tanstack/react-router&#39;&#xA;import { useQuery } from &#39;@tanstack/react-query&#39;&#xA;import { usersQueryOptions } from &#39;~/queries/users&#39;&#xA;&#xA;export const Route = createFileRoute(&#39;/users&#39;)({&#xA;  loader: ({ context }) =&gt;&#xA;    context.queryClient.ensureQueryData(&#xA;      usersQueryOptions,&#xA;    ),&#xA;  component: UsersPage,&#xA;})&#xA;&#xA;function UsersPage() {&#xA;  const { data: users } = useQuery(usersQueryOptions)&#xA;&#xA;  return (&#xA;    &lt;ul&gt;&#xA;      {users?.map((user) =&gt; (&#xA;        &lt;li key={user.id}&gt;{user.name}&lt;/li&gt;&#xA;      ))}&#xA;    &lt;/ul&gt;&#xA;  )&#xA;}The router must expose the appropriate queryClient through its typed context, and the application must configure the Query provider and, when using SSR, the appropriate Router–Query integration.The result is a shared contract:The loader can ensure that data is available before the route renders.The component reads from the same Query cache.Other components can reuse the same query options.Query controls freshness and subsequent refetching.This does not mean a network request can never happen again. A stale query, invalidation, or an explicit refetch can legitimately trigger one. The goal is to avoid unnecessary duplicate fetching and separate caches.When to use this pattern: Lists, detail pages, nested routes, dashboards, and resources shared across multiple components.4. Pattern three: Handle SSR and authenticated requestsServer-side rendering introduces a different problem: the server does not automatically inherit the browser&#39;s request context when it calls an external API.If an upstream API requires the incoming request&#39;s cookies or authorization headers, the HTTP client needs an explicit server-side strategy.tanstack-fetch supports an ssr-forward plugin for forwarding request context.A server-aware client can be configured like this:// Illustrative server-side setup&#xA;import { createFetch } from &#39;tanstack-fetch&#39;&#xA;&#xA;export const createServerApi = (&#xA;  apiOrigin: string,&#xA;  cookie: string | undefined,&#xA;) =&gt;&#xA;  createFetch({&#xA;    baseUrl: `${apiOrigin}/api`,&#xA;    source: &#39;ssr&#39;,&#xA;    plugins: [&#39;ssr-forward&#39;],&#xA;    incoming: () =&gt; ({ cookie }),&#xA;  })This example illustrates the configuration pattern. Obtain the cookie from the actual incoming request using the relevant TanStack Start server API, and create the client in the request&#39;s server-side execution context.For a real application:Use an absolute API origin on the server.Read credentials from the incoming request, not from a global variable.Never expose private environment variables or server-only credentials to the browser.Avoid sharing user-specific API clients or Query caches across SSR requests.Forward only the headers that the upstream service actually needs.A server-side request may also need to preserve an authorization header or request ID. Those should be forwarded deliberately rather than copying every incoming header.When to use this pattern: Personalized SSR pages, authenticated dashboards, and applications whose API uses cookie-based sessions.5. Pattern four: Mutations and cache invalidationFetching data is only half of a data layer. The other half is keeping the UI consistent after writes.Suppose an application creates a user. The mutation should use the same HTTP client and invalidate the query that represents the user list.import {&#xA;  useMutation,&#xA;  useQueryClient,&#xA;} from &#39;@tanstack/react-query&#39;&#xA;import { api } from &#39;~/lib/api&#39;&#xA;&#xA;type CreateUserInput = {&#xA;  name: string&#xA;}&#xA;&#xA;type User = {&#xA;  id: string&#xA;  name: string&#xA;}&#xA;&#xA;export function CreateUserButton() {&#xA;  const queryClient = useQueryClient()&#xA;&#xA;  const createUser = useMutation({&#xA;    mutationFn: (input: CreateUserInput) =&gt;&#xA;      api.post&lt;User&gt;(&#39;/users&#39;, { body: input }),&#xA;&#xA;    onSuccess: () =&gt;&#xA;      queryClient.invalidateQueries({&#xA;        queryKey: [&#39;users&#39;],&#xA;      }),&#xA;  })&#xA;&#xA;  return (&#xA;    &lt;button&#xA;      disabled={createUser.isPending}&#xA;      ={() =&gt;&#xA;        createUser.mutate({ name: &#39;New user&#39; })&#xA;      }&#xA;    &gt;&#xA;      {createUser.isPending ? &#39;Creating...&#39; : &#39;Create user&#39;}&#xA;    &lt;/button&gt;&#xA;  )&#xA;}The example assumes the application has already configured the appropriate Query provider.Invalidation marks matching cached queries as stale and allows active queries to refetch according to Query&#39;s behavior. It does not blindly replace every cached copy of the data.For more responsive interfaces, optimistic updates may be appropriate. However, they need a rollback strategy when the mutation fails and careful handling when concurrent updates are possible.When to use this pattern: Forms, CRUD screens, admin panels, and any UI where a write affects previously fetched data.6. Pattern five: Centralize HTTP errors and authentication behaviorHTTP errors should have predictable semantics across the application.A 401 Unauthorized response might indicate that a session has expired. A 403 Forbidden response generally means that the caller is authenticated but lacks permission. A 404 means the resource was not found.These conditions should not all trigger the same recovery action.A reusable HTTP client gives you a place to configure error handling and interceptors instead of repeating that logic in every query function.For example, a refresh-token interceptor can coordinate token refresh and retry eligible failed requests:import { createFetch } from &#39;tanstack-fetch&#39;&#xA;import {&#xA;  createRefreshTokenInterceptor,&#xA;} from &#39;tanstack-fetch/plugins&#39;&#xA;&#xA;const api = createFetch({&#xA;  baseUrl: &#39;/api&#39;,&#xA;})&#xA;&#xA;api.use(&#xA;  &#39;refresh-token&#39;,&#xA;  createRefreshTokenInterceptor({&#xA;    refresh: async () =&gt; {&#xA;      // Refresh the session using your authentication flow.&#xA;      // Store updated credentials according to your&#xA;      // application&#39;s security model.&#xA;    },&#xA;  }),&#xA;)The refresh implementation is application-specific. It must actually obtain and persist or update the new credential before a retry can succeed.A production implementation should also consider:Concurrent requests that fail while a refresh is in progress.Refresh failure and session expiry.Avoiding recursive refresh requests.Which HTTP methods are safe to retry.Cross-tab session changes.Whether credentials are stored in secure, HttpOnly cookies or another appropriate mechanism.Do not redirect to login on every error. In particular, a 403 should not automatically trigger logout, and a 404 should not be treated as an expired session.When to use this pattern: Applications with authentication, protected APIs, consistent error handling, and several features that share the same backend.7. Pattern six: Combine REST queries with live SSE updatesSome pages need both an initial snapshot and a stream of updates.Examples include notifications, order tracking, live dashboards, and operational monitoring.A useful architecture is:Load the initial resource through REST or another regular API.Use TanStack Query to cache and manage that resource.Open a server-sent events connection in the browser.Update the cache when an event arrives, or invalidate the relevant query when the event is incomplete.tanstack-fetch provides SSE support through its tanstack-fetch/sse integration.The exact event handling depends on the shape of your API. For a complete entity update, you may be able to update the cache directly:queryClient.setQueryData(&#xA;  [&#39;order&#39;, orderId],&#xA;  updatedOrder,&#xA;)If the event only tells you that an order changed, invalidating the query is usually safer:queryClient.invalidateQueries({&#xA;  queryKey: [&#39;order&#39;, orderId],&#xA;})An SSE reconnect does not prove that no events were missed. If you need reliable recovery, design the protocol around event IDs, replay or resume support, and a strategy for reconciling the current snapshot.For authenticated streams, verify how credentials are transported. The browser&#39;s native EventSource API does not provide a general option for setting arbitrary authorization headers; a fetch-based SSE client can be useful when those headers are required.When to use this pattern: Live dashboards, event-driven interfaces, and pages where server state changes without a user action.8. What about Server Functions?TanStack Start Server Functions are useful for server-only logic that your own application invokes across the client/server boundary.For example, they can wrap a database operation or access server-only environment variables.They are not interchangeable with a generic HTTP client.Use a Server Function when the operation belongs to the application&#39;s server logic. Use an HTTP client when communicating with a REST API or another HTTP service. Use Server Routes when you need endpoints intended to be called externally or across origins.If you combine Server Functions with TanStack Query, keep validation, authorization, and cache invalidation explicit. The server boundary does not remove the need for security checks.9. A practical project structureOne possible structure is:src/&#xA;  lib/&#xA;    api.ts&#xA;  queries/&#xA;    users.ts&#xA;    orders.ts&#xA;  routes/&#xA;    users.tsx&#xA;    orders.$orderId.tsx&#xA;  components/&#xA;    create-user-button.tsxThe exact folders are less important than keeping responsibilities clear:lib/api.ts owns reusable HTTP configuration.queries/ owns shared query keys, options, and request functions.routes/ owns navigation and route-level data requirements.components/ owns UI behavior and presentation.For SSR, keep request-specific clients and caches scoped to the request. Do not turn a convenience singleton into a cross-user data leak.10. How should you choose?RequirementRecommended approachSimple browser requestcreateFetch + useQueryRoute-level preloadingqueryOptions + ensureQueryDataAuthenticated SSRRequest-scoped client + ssr-forwardWrites and stale cached datauseMutation + invalidationShared HTTP error policyClient configuration and pluginsLive server updatesSSE + Query cacheServer-only application logicTanStack Start Server FunctionsPublic HTTP endpointServer Routes or a dedicated APIYou do not need to adopt every pattern at once. Start with one API client and shared query options. Add SSR forwarding, mutation strategies, and live updates only when the application needs them.Final thoughtsTanStack Start 1.0 makes this a good moment to evaluate a full-stack React architecture, but a stable framework does not automatically give you a maintainable data layer.The important part is choosing clear boundaries: Router coordinates navigation, Query owns server-state caching, the HTTP client owns transport concerns, and the server owns privileged operations.That&#39;s the problem space tanstack-fetch is designed to address. It is not a replacement for TanStack Query or TanStack Start; it is an option for standardizing HTTP behavior alongside them.Explore the project:tanstack-fetch documentationTanStack Start documentationTanStack Query integration guideIf you&#39;re building with TanStack Start, which part of the data layer has required the most work in your project: SSR, authentication, caching, or real-time updates?</description><category>garmabi.mohammad73</category><author>garmabi.mohammad73</author><pubDate>Wed, 14 Oct 2026 14:59:41 +0330</pubDate></item><item><title>آموزش Angular و tanstack-fetch؛ ساخت HTTP Client تایپ‌سیف با TypeScript</title><link>https://virgool.ir/@garmabi.mohammad73/httpsdevtomwmdgmbangular-api-requests-with-tanstack-fetch-and-tanstack-query-498p-kftslhhtage7</link><description>توضیحات مقاله: در این آموزش یاد می‌گیریم چطور با tanstack-fetch، Angular Dependency Injection و TanStack Query یک لایه‌ی API قابل‌استفاده‌ی مجدد، تایپ‌سیف و پشتیبان از لغو درخواست بسازیم.در پروژه‌های Angular معمولاً کار با API با چند درخواست ساده شروع می‌شود؛ اما با بزرگ‌تر شدن پروژه، مدیریت درخواست‌ها، خطاها، تایپ پاسخ‌ها و لغو درخواست‌ها می‌تواند به یک چالش تبدیل شود.اگر هر کامپوننت منطق درخواست HTTP خودش را داشته باشد، نگهداری و یکپارچه‌سازی رفتارها دشوارتر خواهد شد.در این مقاله، یک روش برای جداسازی لایه‌ی HTTP از مدیریت وضعیت سرور را با استفاده از tanstack-fetch، Angular و TanStack Query بررسی می‌کنیم.tanstack-fetch چیست؟tanstack-fetch یک HTTP client مستقل مبتنی بر TypeScript است که برای استفاده در کنار الگوی برنامه‌نویسی TanStack Query طراحی شده است.مسئولیت ابزارها را می‌توان این‌طور تقسیم کرد:tanstack-fetch: ارسال درخواست HTTP و مدیریت خطاهای مرتبط با کلاینت.TanStack Query: مدیریت چرخه‌ی عمر query و کش داده‌های سمت سرور.Angular: مدیریت کامپوننت‌ها و Dependency Injection.این پروژه توسط Mohammad Garmabi توسعه داده شده و یک پکیج رسمی از تیم TanStack نیست.۱. نصب پکیج‌هاابتدا پکیج‌های موردنیاز را نصب می‌کنیم:npm install tanstack-fetch&#xA;npm install @tanstack/angular-query-experimentalاین نمونه بر اساس API ارائه‌شده در مستندات Angular پروژه نوشته شده است. پیش از استفاده در محیط production، سازگاری نسخه‌های نصب‌شده را بررسی کنید.۲. ساخت HTTP Client مشترکبهتر است تنظیمات API را در یک محل مشخص نگه داریم:// api.ts&#xA;import { createFetch } from &#39;tanstack-fetch&#39;;&#xA;&#xA;export interface User {&#xA;  id: number;&#xA;  name: string;&#xA;  email: string;&#xA;}&#xA;&#xA;export const api = createFetch({&#xA;  baseUrl: &#39;https://jsonplaceholder.typicode.com&#39;,&#xA;});در این مثال از یک API آزمایشی استفاده می‌کنیم. در پروژه‌ی واقعی می‌توانید آدرس API خودتان را قرار دهید.۳. اتصال به Angular Dependency Injectionحالا client را به providerهای برنامه اضافه می‌کنیم:import { bootstrapApplication } from &#39;@angular/platform-browser&#39;;&#xA;import { provideFetchClient } from &#39;tanstack-fetch/angular&#39;;&#xA;&#xA;import { api } from &#39;./api&#39;;&#xA;import { UsersComponent } from &#39;./users.component&#39;;&#xA;&#xA;bootstrapApplication(UsersComponent, {&#xA;  providers: [&#xA;    provideFetchClient({ client: api }),&#xA;  ],&#xA;});پس از این مرحله، می‌توانیم در context معتبر Angular از injectFetch() برای دریافت client استفاده کنیم.۴. دریافت داده با TanStack Queryدر مثال زیر، فهرست کاربران را دریافت می‌کنیم و وضعیت درخواست را در رابط کاربری نمایش می‌دهیم.import { Component } from &#39;@angular/core&#39;;&#xA;import { injectQuery } from &#39;@tanstack/angular-query-experimental&#39;;&#xA;import { injectFetch } from &#39;tanstack-fetch/angular&#39;;&#xA;import { isFetchError } from &#39;tanstack-fetch&#39;;&#xA;&#xA;import { User } from &#39;./api&#39;;&#xA;&#xA;@Component({&#xA;  selector: &#39;app-users&#39;,&#xA;  standalone: true,&#xA;  template: `&#xA;    @if (users.isPending()) {&#xA;      &lt;p&gt;در حال دریافت اطلاعات...&lt;/p&gt;&#xA;    } @else if (users.isError()) {&#xA;      &lt;p&gt;{{ errorLabel() }}&lt;/p&gt;&#xA;    } @else {&#xA;      &lt;ul&gt;&#xA;        @for (user of users.data(); track user.id) {&#xA;          &lt;li&gt;{{ user.name }} - {{ user.email }}&lt;/li&gt;&#xA;        }&#xA;      &lt;/ul&gt;&#xA;    }&#xA;  `,&#xA;})&#xA;export class UsersComponent {&#xA;  private readonly api = injectFetch();&#xA;&#xA;  readonly users = injectQuery(() =&gt; ({&#xA;    queryKey: [&#39;users&#39;],&#xA;    queryFn: ({ signal }: { signal: AbortSignal }) =&gt;&#xA;      this.api.get&lt;User[]&gt;(&#39;/users&#39;, { signal }),&#xA;  }));&#xA;&#xA;  errorLabel(): string {&#xA;    const error = this.users.error();&#xA;&#xA;    return isFetchError(error)&#xA;      ? `خطای HTTP ${error.status}: ${error.message}`&#xA;      : &#39;دریافت اطلاعات ناموفق بود&#39;;&#xA;  }&#xA;}این معماری چه مزایایی دارد؟۱. تایپ‌سیف بودن پاسخ‌هابا استفاده از get&lt;User[]&gt; نوع داده‌ی موردانتظار را مشخص می‌کنیم. این کار به خوانایی کد و تشخیص خطاهای تایپی کمک می‌کند؛ البته صحت واقعی داده‌های دریافتی از سرور را به‌تنهایی تضمین نمی‌کند.۲. استفاده از Dependency Injectionبه‌جای ساختن client در هر کامپوننت، از یک نمونه‌ی پیکربندی‌شده استفاده می‌کنیم.۳. لغو درخواست با AbortSignalسیگنال دریافتی از TanStack Query را به درخواست HTTP منتقل می‌کنیم:queryFn: ({ signal }: { signal: AbortSignal }) =&gt;&#xA;  this.api.get&lt;User[]&gt;(&#39;/users&#39;, { signal })این اتصال اجازه می‌دهد درخواست در صورت لغو شدن توسط چرخه‌ی عمر query، عملیات لغو را دریافت کند؛ مشروط به اینکه HTTP client سیگنال را به درخواست زیرین منتقل کند.۴. جداسازی مدیریت خطاتابع isFetchError() کمک می‌کند خطاهای مربوط به HTTP client را از خطاهای دیگر متمایز کنیم و پیام مناسب‌تری به کاربر نمایش دهیم.۵. جداسازی HTTP از مدیریت وضعیت سرورTanStack Query مسئول کش و چرخه‌ی عمر query است، درحالی‌که tanstack-fetch درخواست HTTP را انجام می‌دهد. این تفکیک از ترکیب شدن مسئولیت‌های متفاوت در یک کامپوننت جلوگیری می‌کند.آیا همیشه باید از یک HTTP Client جداگانه استفاده کنیم؟خیر. Angular ابزار داخلی HttpClient را هم در اختیار توسعه‌دهندگان قرار می‌دهد و برای بسیاری از پروژه‌ها کاملاً کافی است.استفاده از یک client جداگانه زمانی ارزشمند است که به قراردادهای مشترک برای درخواست‌ها، تایپ پاسخ‌ها، مدیریت خطا یا یکپارچگی بیشتر با ابزارهای مدیریت server state نیاز داشته باشید.هدف، پیچیده‌تر کردن معماری نیست؛ هدف این است که هر لایه مسئولیت مشخصی داشته باشد.جمع‌بندیبا ترکیب Angular Dependency Injection، tanstack-fetch و TanStack Query می‌توان یک لایه‌ی API ساخت که درخواست‌های تایپ‌سیف را با مدیریت وضعیت سرور و امکان انتقال سیگنال لغو هماهنگ می‌کند.اگر می‌خواهید جزئیات API و مثال‌های بیشتر را ببینید، مستندات پروژه در دسترس است:راهنمای Angular در tanstack-fetchمخزن GitHub پروژهنویسنده: Mohammad Garmabi، توسعه‌دهنده‌ی نرم‌افزار و سازنده‌ی پروژه‌ی متن‌باز tanstack-fetch.</description><category>garmabi.mohammad73</category><author>garmabi.mohammad73</author><pubDate>Mon, 12 Oct 2026 10:02:51 +0330</pubDate></item><item><title>انتشار tanstack-fetch 1.7.0؛ آداپترهای Solid، Angular و Svelte + دستور Doctor</title><link>https://virgool.ir/@garmabi.mohammad73/انتشار-tanstack-fetch-170-آداپترهای-solid-angular-و-svelte-دستور-doctor-jpicuhoimeno</link><description>در نسخه 1.7.0 کتابخانه tanstack-fetch، پشتیبانی از فریم‌ورک‌های بیشتری اضافه شده است. این نسخه شامل آداپترهای Solid، Angular و Svelte، ابزار بررسی سلامت پروژه (Doctor)، بهبود ابزار مهاجرت، نمایش پیشرفت دانلود و فیلتر فریم‌ورک در مستندات است.اگر از TanStack Query استفاده می‌کنید، تابع queryFn باید سه اصل مهم را رعایت کند:داده را برگرداند، نه آبجکت خام Response.در صورت بروز خطا، آن را با throw اعلام کند تا وضعیت isError به‌درستی کار کند.از AbortSignal پشتیبانی کند تا درخواست‌ها در صورت نیاز لغو شوند.tanstack-fetch با همین رویکرد طراحی شده است: ساده‌سازی لایه HTTP و هماهنگی بهتر آن با الگوی استفاده از TanStack Query.توجه: tanstack-fetch یک پروژه مستقل و غیررسمی است و ارتباطی با تیم رسمی TanStack ندارد.آداپترهای جدیددر نسخه 1.7.0، آداپترهای Solid، Svelte و Angular اضافه شده‌اند تا بتوان از قابلیت‌های کتابخانه در این فریم‌ورک‌ها استفاده کرد.Solidمسیر ایمپورت:tanstack-fetch/solidAPIهای اصلی:FetchProvideruseFetchuseSseSvelteمسیر ایمپورت:tanstack-fetch/svelteAPIهای اصلی:setFetchClientuseFetchuseSseAngularمسیر ایمپورت:tanstack-fetch/angularAPIهای اصلی:provideFetchClientinjectFetchuseSseمدیریت وضعیت اتصال SSE نیز از یک الگوی یکسان پیروی می‌کند:connecting، connected، disconnected و errorاین وضعیت‌ها کمک می‌کنند شرایط اتصال و خطاهای ارتباطی را در برنامه بهتر مدیریت کنید.مستندات آداپتر Solid:https://mohamadgarmabi.github.io/tanstack-fetch/guide/solidابزار Doctor برای بررسی سلامت پروژهیکی از قابلیت‌های جدید این نسخه، دستور doctor است.با اجرای این دستور، می‌توانید وضعیت استفاده از کلاینت‌های HTTP و برخی مشکلات احتمالی مهاجرت را در پروژه بررسی کنید.برای اجرا:npx tanstack-fetch doctor --dir ./src&#xA;این ابزار موارد زیر را بررسی می‌کند:وجود پکیج‌های قدیمی مانند Axios، ky و ofetchتعداد استفاده از createFetchایمپورت‌های باقی‌مانده از کتابخانه‌های قبلیتوابع queryFn که احتمالاً signal را به درخواست منتقل نمی‌کنندهدف این است که پیش از تکمیل مهاجرت، مشکلات احتمالی را شناسایی کنید و بررسی کد را سریع‌تر انجام دهید.بهبود دستور Migrateابزار مهاجرت حالا اسکفولدهای بیشتری برای فریم‌ورک‌های مختلف ارائه می‌دهد.برای مهاجرت از Axios به Solid:npx tanstack-fetch migrate --from axios --framework solid --write&#xA;برای مهاجرت از Axios به Angular:npx tanstack-fetch migrate --from axios --framework angular --write&#xA;برای مهاجرت از Fetch به Svelte:npx tanstack-fetch migrate --from fetch --framework svelte --write&#xA;برای مهاجرت از Fetch به SvelteKit:npx tanstack-fetch migrate --from fetch --framework sveltekit --write&#xA;این دستورها با هدف کمک به شروع مهاجرت و ایجاد ساختار مناسب برای فریم‌ورک مقصد ارائه شده‌اند. بعد از اجرا، کد تولیدشده را بررسی کنید و بخش‌هایی را که به منطق اختصاصی پروژه وابسته‌اند، به‌صورت دستی اصلاح کنید.راهنمای مهاجرت:https://mohamadgarmabi.github.io/tanstack-fetch/guide/migrateنمایش پیشرفت دانلود فایلقابلیت جدید onDownloadProgress امکان مشاهده پیشرفت دانلود را فراهم می‌کند.برای مثال، هنگام دانلود یک فایل PDF:await api.get(&#39;/files/report.pdf&#39;, {&#xA;  parseAs: &#39;blob&#39;,&#xA;  onDownloadProgress: ({ progress, loaded, total }) =&gt; {&#xA;    console.log(progress, loaded, total)&#xA;  },&#xA;})&#xA;در این مثال:progress میزان پیشرفت دانلود را نشان می‌دهد.loaded حجم دریافت‌شده را مشخص می‌کند.total حجم کل فایل را، در صورت در دسترس بودن، نشان می‌دهد.این قابلیت برای نمایش نوار پیشرفت دانلود فایل‌ها و بهبود تجربه کاربری مفید است.راهنمای آپلود و دانلود:https://mohamadgarmabi.github.io/tanstack-fetch/guide/uploadنصب یا ارتقا به نسخه 1.7.0برای نصب نسخه جدید، دستور زیر را اجرا کنید:npm install tanstack-fetch@1.7.0لینک‌های پروژهوبلاگ نسخه 1.7.0: https://mohamadgarmabi.github.io/tanstack-fetch/blog/tanstack-fetch-1-7-0راهنمای مهاجرت: https://mohamadgarmabi.github.io/tanstack-fetch/guide/migrateراهنمای آپلود و دانلود: https://mohamadgarmabi.github.io/tanstack-fetch/guide/uploadمستندات Solid: https://mohamadgarmabi.github.io/tanstack-fetch/guide/solidصفحه npm: https://www.npmjs.com/package/tanstack-fetchمخزن GitHub: https://github.com/mohamadgarmabi/tanstack-fetchنسخه 1.7.0 با هدف گسترش پشتیبانی از فریم‌ورک‌ها، ساده‌تر کردن مهاجرت و فراهم کردن ابزارهای بهتر برای بررسی و مدیریت درخواست‌های HTTP منتشر شده است.</description><category>garmabi.mohammad73</category><author>garmabi.mohammad73</author><pubDate>Fri, 09 Oct 2026 18:07:20 +0330</pubDate></item><item><title>tanstack-fetch در برابر ky و Axios؛ کدام HTTP Client برای React و TanStack Query مناسب‌تر است؟</title><link>https://virgool.ir/@garmabi.mohammad73/tanstack-fetch-در-برابر-ky-و-axios-کدام-http-client-برای-react-و-tanstack-query-مناسب-تر-است-b3nn0wedfhof</link><description>اگر با React کار می‌کنید، احتمالاً بارها بین fetch، Axios، ky یا HTTP Clientهای دیگر انتخاب کرده‌اید.اما وقتی TanStack Query وارد معماری پروژه می‌شود، یک سؤال مهم‌تر مطرح می‌شود:آیا HTTP Client ما واقعاً با مدل کاری TanStack Query هماهنگ است؟همین سؤال یکی از دلایلی بود که من tanstack-fetch را ساختم.هدف tanstack-fetch این نیست که صرفاً یک HTTP Client دیگر باشد.ایده اصلی این است:TanStack Query مسئول Server State باشد و tanstack-fetch مسئول HTTP Transport.مشکل کجاست؟TanStack Query برای queryFn یک مدل بسیار ساده دارد.queryFn باید:داده را برگردانددر صورت خطا exception ایجاد کنداز AbortSignal پشتیبانی کندمثلاً با Fetch خام:useQuery({&#xA;  queryKey: [&#39;users&#39;],&#xA;  queryFn: ({ signal }) =&gt;&#xA;    fetch(&#39;/api/users&#39;, { signal }).then(res =&gt; res.json()),&#xA;})&#xA;در پروژه واقعی، اما HTTP Client معمولاً مسئولیت‌های بیشتری پیدا می‌کند:AuthenticationToken RefreshInterceptorError HandlingSSRSSERetryType SafetyRequest Cancellationاینجاست که Axios یا ky وارد می‌شوند.Axios چه مشکلی دارد؟استفاده از Axios در TanStack Query کاملاً ممکن است:queryFn: async ({ signal }) =&gt; {&#xA;  const { data } = await axios.get&lt;User[]&gt;(&#39;/users&#39;, {&#xA;    signal,&#xA;  })&#xA;&#xA;  return data&#xA;}&#xA;اما یک تبدیل اضافه داریم:Axios Response&#xA;      ↓&#xA;response.data&#xA;      ↓&#xA;TanStack Query&#xA;این تبدیل کوچک به‌تنهایی مشکل بزرگی نیست.اما در یک پروژه بزرگ، ممکن است صدها بار تکرار شود.ky چطور؟ky نسبت به Axios سبک‌تر و نزدیک‌تر به Fetch است:queryFn: ({ signal }) =&gt;&#xA;  ky.get(&#39;/users&#39;, { signal }).json&lt;User[]&gt;()&#xA;اما همچنان باید Response را به داده تبدیل کنیم:Fetch Response&#xA;      ↓&#xA;.json()&#xA;      ↓&#xA;TanStack Query&#xA;این API کاملاً قابل قبول است و ky برای بسیاری از پروژه‌ها انتخاب خوبی است.اما اگر TanStack Query بخش اصلی Server State پروژه باشد، می‌توان سؤال دیگری مطرح کرد:آیا می‌توان HTTP Client را مستقیماً با مدل TanStack Query هماهنگ کرد؟tanstack-fetchایده tanstack-fetch دقیقاً همین است.مثلاً:const api = createFetch({&#xA;  baseUrl: &#39;https://api.example.com&#39;,&#xA;})&#xA;و بعد:useQuery({&#xA;  queryKey: [&#39;users&#39;],&#xA;  queryFn: ({ signal }) =&gt;&#xA;    api.get&lt;User[]&gt;(&#39;/users&#39;, { signal }),&#xA;})&#xA;نتیجه مستقیماً داده است:User[]&#xA;بنابراین خبری از این موارد نیست:response.data&#xA;یا:response.json()&#xA;خطاهای HTTP نیز از طریق FetchError مدیریت می‌شوند.معماری پیشنهادیهدف این نیست که tanstack-fetch جای TanStack Query را بگیرد.برعکس، هر کدام مسئولیت مشخص خودشان را دارند:┌─────────────────────────┐&#xA;│        React UI         │&#xA;├─────────────────────────┤&#xA;│      TanStack Query     │&#xA;│                         │&#xA;│ Server State            │&#xA;│ Cache                   │&#xA;│ Retry                   │&#xA;│ Refetch                 │&#xA;│ Mutations               │&#xA;├─────────────────────────┤&#xA;│      tanstack-fetch     │&#xA;│                         │&#xA;│ HTTP Transport          │&#xA;│ Type Safety             │&#xA;│ Authentication          │&#xA;│ Token Refresh           │&#xA;│ Interceptors            │&#xA;│ SSE                     │&#xA;├─────────────────────────┤&#xA;│          Fetch          │&#xA;└─────────────────────────┘&#xA;به این شکل، هر لایه یک مسئولیت مشخص دارد.Token Refresh و درخواست‌های همزمانیکی از مشکلات رایج در اپلیکیشن‌های واقعی، Refresh Token است.فرض کنید چند درخواست تقریباً همزمان با 401 مواجه شوند.بدون هماهنگی ممکن است اتفاقی شبیه این رخ دهد:Request A → 401 → refresh&#xA;Request B → 401 → refresh&#xA;Request C → 401 → refresh&#xA;Request D → 401 → refresh&#xA;در حالی که چیزی که معمولاً می‌خواهیم این است:Request A → 401 ─┐&#xA;Request B → 401 ─┤&#xA;Request C → 401 ─┼→ یک refresh → retry&#xA;Request D → 401 ─┘&#xA;tanstack-fetch برای این سناریو یک Refresh Token Interceptor با الگوی single-flight دارد.همچنین امکان Refresh قبل از منقضی شدن Token و Refresh واکنشی بعد از اولین 401 را فراهم می‌کند.SSR و SSEدر اپلیکیشن‌های مدرن React، HTTP Request فقط به Browser محدود نیست.در محیط‌هایی مثل Next.js ممکن است با مواردی مثل:SSRCookie ForwardingServer-side RequestAuthenticationSSERequest Cancellationمواجه شویم.بنابراین HTTP Client باید بتواند این سناریوها را نیز پوشش دهد.tanstack-fetch برای مواردی مانند SSR Cookie Forwarding، Authenticated SSE و SSE Resume قابلیت‌هایی ارائه می‌کند.مقایسه tanstack-fetch، ky و Axiosتمرکز روی TanStack Querytanstack-fetch:پشتیبانی و طراحی‌شده با تمرکز روی TanStack Queryky:تمرکز مستقیمی روی TanStack Query نداردAxios:تمرکز مستقیمی روی TanStack Query نداردمبتنی بر Fetchtanstack-fetch:بلهky:بلهAxios:خیرTyped Errortanstack-fetch:بله، با FetchErrorky:بله، با HTTPErrorAxios:بله، با AxiosErrorAbortSignaltanstack-fetch:پشتیبانی می‌کندky:پشتیبانی می‌کندAxios:پشتیبانی می‌کندToken Refreshtanstack-fetch:Built-inky:نیاز به پیاده‌سازی سفارشی داردAxios:نیاز به پیاده‌سازی سفارشی داردSingle-flight Refreshtanstack-fetch:پشتیبانی می‌کندky:نیاز به پیاده‌سازی سفارشی داردAxios:نیاز به پیاده‌سازی سفارشی داردSSRtanstack-fetch:First-class supportky:نیاز به پیاده‌سازی دستیAxios:نیاز به پیاده‌سازی دستیSSEtanstack-fetch:پشتیبانی می‌کندky:نیاز به پیاده‌سازی سفارشی داردAxios:نیاز به پیاده‌سازی سفارشی دارداکوسیستمtanstack-fetch:جدیدky:بالغAxios:بسیار بالغالبته این جدول به این معنی نیست که یک کتابخانه برای همه پروژه‌ها مناسب‌تر است.هر کدام مسئله متفاوتی را هدف قرار می‌دهند.چه زمانی tanstack-fetch انتخاب مناسبی است؟اگر:از TanStack Query استفاده می‌کنیدمی‌خواهید queryFn ساده‌تر شودType Safety برایتان مهم استToken Refresh داریدبا SSR کار می‌کنیدبه SSE نیاز داریدپروژه جدیدی شروع کرده‌ایدیا قصد مهاجرت از Axios، ky، ofetch یا Fetch را داریدtanstack-fetch می‌تواند گزینه‌ای باشد که ارزش بررسی دارد.چه زمانی ky بهتر است؟اگر فقط یک Fetch Wrapper سبک و ساده می‌خواهید و وابستگی خاصی به TanStack Query ندارید، ky می‌تواند انتخاب مناسبی باشد.چه زمانی Axios را نگه داریم؟اگر یک پروژه بزرگ با Axios دارید و:Interceptorهای پیچیده داریداکوسیستم بزرگی حول Axios ساخته‌ایدتیم شما تجربه زیادی با Axios داردو هزینه Migration بیشتر از مزایای آن استدلیلی وجود ندارد صرفاً به خاطر یک معماری جدید همه چیز را Rewrite کنید.مشکل واقعی: Migrationانتخاب HTTP Client جدید معمولاً سخت‌ترین قسمت ماجرا نیست.Migration سخت‌تر است.در یک پروژه واقعی ممکن است این موارد همزمان وجود داشته باشند:axios.get(...)&#xA;axios.post(...)&#xA;fetch(...)&#xA;ky.get(...)&#xA;در صدها فایل مختلف.به همین دلیل tanstack-fetch یک Migration CLI نیز دارد.این CLI می‌تواند به شناسایی HTTP Call Siteهای موجود کمک کند و مسیر مهاجرت از:Axios&#xA;ky&#xA;ofetch&#xA;Fetch&#xA;به معماری سازگارتر با TanStack Query را ساده‌تر کند.هدف این نیست که کل پروژه را یک‌باره Rewrite کنید.هدف، Incremental Migration است.آیا باید Axios را کنار بگذاریم؟لزوماً نه.اگر پروژه شما با Axios به‌خوبی کار می‌کند، احتمالاً نیازی به Rewrite کردن آن ندارید.سؤال جالب‌تر این است:وقتی TanStack Query مرکز Server State پروژه ماست، چرا HTTP Client باید باعث شود در هر queryFn یک لایه Adapter اضافی داشته باشیم؟tanstack-fetch دقیقاً برای پاسخ دادن به همین سؤال ساخته شده است.جمع‌بندیAxios یک HTTP Client بسیار بالغ و قدرتمند است.ky یک Fetch Wrapper سبک و تمیز است.tanstack-fetch رویکرد متفاوتی دارد:HTTP Layer را با Mental Model تان‌استک Query هماهنگ می‌کند.در نتیجه معماری می‌تواند به شکل زیر باشد:TanStack Query&#xA;↓&#xA;Server State&#xA;&#xA;tanstack-fetch&#xA;↓&#xA;HTTP Transport&#xA;&#xA;Fetch&#xA;↓&#xA;Network Primitive&#xA;اگر پروژه جدیدی با React و TanStack Query شروع می‌کنید، این مدل معماری ارزش بررسی دارد.و اگر پروژه فعلی شما با Axios یا ky نوشته شده، لازم نیست همه چیز را یک‌باره تغییر دهید.می‌توانید با چند API شروع کنید، چند queryFn را مهاجرت دهید و بعد Developer Experience و هزینه نگهداری را مقایسه کنید.tanstack-fetch یک پروژه Open Source است و پروژه رسمی TanStack نیست.</description><category>garmabi.mohammad73</category><author>garmabi.mohammad73</author><pubDate>Wed, 07 Oct 2026 13:15:15 +0330</pubDate></item><item><title>مهاجرت از Axios، Ky، Ofetch یا Fetch به tanstack-fetch با یک دستور</title><link>https://virgool.ir/@garmabi.mohammad73/httpsmohamadgarmabigithubiotanstack-fetchblogtanstack-fetch-1-6-2-xclf2dgdtr8v</link><description>یک کلاینت HTTP مناسب TanStack Query، به‌همراه CLI برای پیدا کردن call siteها و اسکفولد React، Vue، Nuxt و Next.jsاگر از TanStack Query استفاده می‌کنید، queryFn عملاً از شما سه چیز می‌خواهد:دیتا برگرداند، نه Response خامروی خطا throw کند تا isError و error handling درست کار کنندAbortSignal را رعایت کند تا cancellation درخواست‌ها درست انجام شودبیشتر HTTP Clientها دقیقاً برای این مدل طراحی نشده‌اند. معمولاً باید res.data را باز کنید، روی Response متد .json() را صدا بزنید یا حواستان باشد signal را به درخواست منتقل کنید.tanstack-fetch برای همین مدل ذهنی ساخته شده است: یک HTTP client تایپ‌شده که با الگوی استفاده از TanStack Query هماهنگ است.در نسخه 1.6.2 یک CLI برای migration اضافه شده که می‌تواند axios، ky، ofetch و fetch را اسکن کند، الگوهای امن را rewrite کند و برای React، Vue، Nuxt و Next.js فایل‌های اولیه را scaffold کند.نکته: tanstack-fetch یک پروژه رسمی TanStack نیست و هیچ وابستگی رسمی به تیم TanStack ندارد؛ فقط API و mental model آن با هدف هماهنگی با اکوسیستم TanStack طراحی شده است.مستندات:راهنمای Migration · بلاگ · npmقبلqueryFn: async ({ signal }) =&gt; {&#xA;  const res = await axios.get(&#39;/users&#39;, {&#xA;    signal,&#xA;    params: { page: 1 }&#xA;  })&#xA;&#xA;  return res.data&#xA;}&#xA;بعدimport { createFetch } from &#39;tanstack-fetch&#39;&#xA;&#xA;const api = createFetch({&#xA;  baseUrl: &#39;https://api.example.com&#39;&#xA;})&#xA;&#xA;queryFn: ({ signal }) =&gt;&#xA;  api.get&lt;User[]&gt;(&#39;/users&#39;, {&#xA;    signal,&#xA;    query: { page: 1 }&#xA;  })&#xA;یک کلاینت، دیتای مستقیم، FetchError تایپ‌شده و پشتیبانی از AbortSignal از ابتدا.CLI Migrationابتدا پکیج را نصب کنید:npm install tanstack-fetch&#xA;بعد می‌توانید migration را به‌صورت dry run اجرا کنید:npx tanstack-fetch migrate --from axios --dir ./src&#xA;&#xA;npx tanstack-fetch migrate --from all --dir ./src&#xA;یا migration و scaffold را هم‌زمان انجام دهید:npx tanstack-fetch migrate \&#xA;  --from axios \&#xA;  --framework react \&#xA;  --dir ./src \&#xA;  --write&#xA;&#xA;npx tanstack-fetch migrate \&#xA;  --from ofetch \&#xA;  --framework nuxt \&#xA;  --write&#xA;&#xA;npx tanstack-fetch migrate \&#xA;  --from fetch \&#xA;  --framework nextjs \&#xA;  --write&#xA;فلگ‌هافلگمعنی--fromaxios | ky | ofetch | fetch | all--frameworkreact | vue | nuxt | nextjs--provider / --no-providerفقط React؛ با یا بدون FetchProvider--writerewrite امن + scaffold--write عمداً محافظه‌کار است.الگوهای شناخته‌شده و امن rewrite می‌شوند؛ اما مواردی مثل پارامترهای پیچیده query، interceptorها یا منطق res.ok ممکن است فقط گزارش شوند تا خودتان آن‌ها را بررسی و اصلاح کنید.بنابراین بعد از migration حتماً گزارش CLI را مرور کنید.نگاشت سریع APIازبهaxios.create({ baseURL })createFetch({ baseUrl })res.dataخود api.get دیتا را برمی‌گرداند.json()خود api.get دیتا را برمی‌گرداندparams در AxiosquerysearchParams در KyqueryisAxiosErrorisFetchErrorدر مورد fetch خام، migration بیشتر گزارش‌محور است.CLI محل‌هایی مثل:fetch(...)&#xA;و:res.ok&#xA;را پیدا می‌کند و گزارش می‌دهد تا بتوانید آن بخش را متناسب با منطق پروژه خودتان بازنویسی کنید.اسکفولد برای فریم‌ورک‌های مختلفCLI می‌تواند بر اساس فریم‌ورک شما ساختار اولیه را هم ایجاد کند:فریم‌ورکخروجیReactapi.ts + queries/users.ts + Provider اختیاریVueapi.ts + pluginNuxtapi + plugin + useApiNext.jsclient + api.server.ts با cookie و ssr-forwardReact و FetchProviderدر React، اگر migration را با --write اجرا کنید، CLI می‌تواند درباره استفاده از FetchProvider از شما سؤال کند:Use FetchProvider for React? [y/N]&#xA;پیش‌فرض N است؛ یعنی بدون Provider.در این حالت می‌توانید client را مستقیماً به queryها پاس بدهید:client: api&#xA;اگر نمی‌خواهید CLI سؤال بپرسد، می‌توانید صریحاً یکی از این دو فلگ را استفاده کنید:--provider&#xA;یا:--no-provider&#xA;چرا tanstack-fetch؟هدف اصلی این پروژه ساده است: HTTP layer باید با مدل استفاده از TanStack Query هماهنگ باشد.چند قابلیت مهم:مناسب برای استفاده مستقیم در queryFn و mutationFnخطاهای تایپ‌شده با اطلاعاتی مثل status و bodyhandlerهای قابل تنظیم برای status codeهای مختلف، از جمله 401 تا 429refresh token و جلوگیری از refreshهای هم‌زمانپشتیبانی از SSR و forwarding کوکیSSE با Authorization و قابلیت resumeپشتیبانی از AbortSignalهسته HTTP حدود 4.8KB gzipاگر می‌خواهید تفاوت آن با Axios، Ky، Ofetch و Fetch را دقیق‌تر ببینید:مقایسه کامل HTTP Clientهاامتحان کنیداگر می‌خواهید ابتدا ببینید پروژه شما چه چیزهایی برای migration دارد، بدون تغییر فایل‌ها شروع کنید:npx tanstack-fetch migrate \&#xA;  --from all \&#xA;  --dir ./src&#xA;و اگر آماده rewrite و scaffold هستید:npx tanstack-fetch migrate \&#xA;  --from all \&#xA;  --framework react \&#xA;  --write \&#xA;  --no-provider&#xA;لینک‌هاسایت tanstack-fetchبلاگ نسخه 1.6.2راهنمای MigrationGitHubnpmاگر روی یک پروژه واقعی این CLI را اجرا کردید، خوشحال می‌شوم بدانم چه migrationهایی را درست تشخیص داد، چه چیزهایی را از دست داد و کجا نیاز به اصلاح دستی داشت.همین بازخوردها می‌توانند مشخص کنند که transformerهای نسخه‌های بعدی باید روی چه الگوهایی تمرکز کنند.</description><category>garmabi.mohammad73</category><author>garmabi.mohammad73</author><pubDate>Mon, 05 Oct 2026 12:34:23 +0330</pubDate></item><item><title>tanstack-fetch 1.6.1؛ تایپ‌گذاری Path Params در API</title><link>https://virgool.ir/@garmabi.mohammad73/httpsmohamadgarmabigithubiotanstack-fetchblogtanstack-fetch-1-6-vf5bmqmpe0xr</link><description>tanstack-fetch 1.6.1 (params typed)نسخه 1.6.1 پکیج tanstack-fetch منتشر شد.تمرکز اصلی این نسخه روی بهبود Type Safety در URL Parameters است؛ به‌خصوص زمانی که API شما مسیرهایی مانند /users/:id یا /users/{id} دارد.در این نسخه، دیگر لازم نیست برای تعریف مسیرهای API یک Route Map جداگانه ایجاد کنید.چه چیزی تغییر کرده؟در نسخه 1.6.0، یک Route Map با createFetch&lt;Routes&gt;() معرفی شده بود.در نسخه 1.6.1 این رویکرد حذف شده و به‌جای آن، params مستقیماً در کنار Type مربوط به Response تعریف می‌شود.مثلاً:type User = {&#xA;  id: string&#xA;  name: string&#xA;}&#xA;&#xA;type Params = {&#xA;  id: number&#xA;}&#xA;&#xA;await api.get&lt;User, Params&gt;(&#39;/users/:id&#39;, {&#xA;  params: { id },&#xA;})&#xA;حالا TypeScript می‌تواند بررسی کند که:پارامتر id واقعاً در مسیر وجود داشته باشد.params شامل کلید موردنیاز مسیر باشد.نوع پارامتر صحیح باشد.پارامتر اضافی که در URL وجود ندارد استفاده نشود.برای مثال، این کد خطا خواهد داشت:await api.get&lt;User, Params&gt;(&#39;/users/:id&#39;, {&#xA;  params: {},&#xA;})&#xA;یا:await api.get&lt;User, { userId: number }&gt;(&#39;/users/:id&#39;, {&#xA;  params: { userId: 1 },&#xA;})&#xA;چون :id در مسیر وجود دارد و userId با آن مطابقت ندارد.Type-safe Body برای POST، PUT و PATCHدر متدهای write نیز امکان مشخص کردن Type مربوط به body اضافه شده است.مثلاً:await api.post&lt;User, NoParams, { name: string }&gt;(&#39;/users&#39;, {&#xA;  body: {&#xA;    name: &#39;Ada&#39;,&#xA;  },&#xA;})&#xA;یا برای یک مسیر دارای parameter:await api.put&lt;User, Params, { name: string }&gt;(&#39;/users/:id&#39;, {&#xA;  params: {&#xA;    id,&#xA;  },&#xA;  body: {&#xA;    name: &#39;Ada&#39;,&#xA;  },&#xA;})&#xA;این الگو برای POST، PUT، PATCH و request قابل استفاده است و در DELETE و upload نیز همین مدل type-safe برای params در دسترس است.چرا دو یا سه Generic؟این بخش به یک محدودیت TypeScript برمی‌گردد.وقتی فقط Response Type را مشخص می‌کنیم:api.get&lt;User&gt;(&#39;/users/:id&#39;)&#xA;TypeScript ممکن است literal بودن مسیر را از دست بدهد و در نتیجه بررسی :id دیگر به شکل مورد انتظار انجام نشود.به همین دلیل در نسخه 1.6.1 می‌توانیم Typeهای مختلف را جداگانه مشخص کنیم:api.get&lt;&#xA;  User,                 // Response&#xA;  { id: number }        // Params&#xA;&gt;(&#39;/users/:id&#39;, {&#xA;  params: { id },&#xA;})&#xA;و در متدهایی که Body دارند:api.post&lt;&#xA;  User,                 // Response&#xA;  NoParams,             // Params&#xA;  { name: string }      // Body&#xA;&gt;(&#39;/users&#39;, {&#xA;  body: {&#xA;    name: &#39;Ada&#39;,&#xA;  },&#xA;})&#xA;تفاوت 1.6.0 و 1.6.1نسخه 1.6.0نسخه 1.6.1Route Map با createFetch&lt;Routes&gt;()حذف Route Mapparams نسبتاً آزادparams کاملاً type-safeتعریف مسیرها در Route MapType پارامتر کنار Response—Type-safe Body برای write methodsارتقا به نسخه جدیداگر از نسخه‌های قبلی استفاده می‌کنید:npm install tanstack-fetch@1.6.1&#xA;نسخه 1.6.1 بیشتر از اینکه یک تغییر ظاهری در API باشد، روی یک مسئله مهم تمرکز دارد:هرچه بیشتر بتوانیم قرارداد API را در زمان توسعه توسط TypeScript بررسی کنیم، خطاهای runtime کمتری خواهیم داشت.این نسخه تلاش می‌کند همین موضوع را برای URL parameters و request body ساده‌تر و صریح‌تر کند.پروژه tanstack-fetch یک HTTP client تایپ‌شده بر پایه Fetch API است که برای استفاده در پروژه‌های TypeScript و در کنار TanStack Query طراحی شده است.tanstack-fetch یک پروژه مستقل است و پکیج رسمی TanStack نیست.اگر از tanstack-fetch استفاده می‌کنید، خوشحال می‌شوم تجربه‌تان درباره طراحی API و Type Safety را بدانم.</description><category>garmabi.mohammad73</category><author>garmabi.mohammad73</author><pubDate>Sat, 03 Oct 2026 10:42:04 +0330</pubDate></item><item><title>tanstack-fetch 1.5.0؛ پشتیبانی از Vue 3 و Nuxt</title><link>https://virgool.ir/@garmabi.mohammad73/httpsmohamadgarmabigithubiotanstack-fetchblogtanstack-fetch-1-5-hc0hvngimn52</link><description>tanstack-fetch 1.5.0؛ پشتیبانی از Vue 3 و Nuxtنسخه 1.5.0 از tanstack-fetch منتشر شد.در این نسخه، پشتیبانی رسمی از Vue 3 و Nuxt اضافه شده است؛ با همان مدل ذهنیای که کاربران TanStack Query به آن عادت دارند:در حالت موفق، داده را برگرداندر خطای HTTP، یک FetchError پرتاب کنAbortSignal را به درخواست منتقل کنهدف tanstack-fetch این است که یک HTTP client کوچک، typed و قابل استفاده در کنار TanStack Query باشد؛ بدون اینکه خودش نقش data-fetching library را بازی کند.نکته: tanstack-fetch یک پکیج رسمی TanStack نیست. این پروژه مستقل است و با الهام از مدل ذهنی TanStack Query طراحی شده است.در نسخه 1.5.0 چه چیزی اضافه شده؟مهمترین تغییر این نسخه، اضافه شدن helperهای رسمی برای Vue 3 و Nuxt است.ExportکاربردcreateFetchPluginنصب کلاینت با app.use یا استفاده در NuxtprovideFetchClientProvide کردن کلاینت در کامپوننت والدuseFetchدریافت کلاینت مشترک از injectuseSseاتصال به SSE و مدیریت وضعیت streamیکی از نکات مهم این طراحی این است که helperهای Vue باعث نمیشوند هستهی HTTP بزرگتر و پیچیدهتر شود.هستهی HTTP در حدود 4.81KB gzip است و قابلیتهایی مثل SSE، پلاگینها، React، Vue، tRPC و DevTools به صورت جداگانه قابل import هستند.useFetch قرار نیست HTTP Request بزنداگر با Nuxt کار کرده باشید، احتمالاً اسم useFetch برایتان آشناست.اما useFetch در tanstack-fetch/vue با useFetch خود Nuxt یک تفاوت اساسی دارد.این helper فقط کلاینتی را که قبلاً provide شده دریافت میکند:import { useFetch as useFetchClient } from &#39;tanstack-fetch/vue&#39;بنابراین:const api = useFetch()به معنی ارسال یک HTTP request نیست.در واقع api همان instance مربوط به createFetch است که از طریق Vue inject دریافت شده است.این تفکیک باعث میشود مسئولیتها مشخص باقی بمانند:createFetch → مدیریت HTTPuseFetch → دسترسی به clientTanStack Query → مدیریت server stateuseSse → مدیریت SSEاستفاده در Vue 3ابتدا یک client ایجاد میکنیم:import { createFetch } from &#39;tanstack-fetch&#39;&#xA;import { createFetchPlugin, useFetch } from &#39;tanstack-fetch/vue&#39;&#xA;&#xA;const api = createFetch({&#xA;  baseUrl: import.meta.env.VITE_API_URL,&#xA;  getToken: () =&gt; localStorage.getItem(&#39;access_token&#39;),&#xA;})&#xA;&#xA;createApp(App)&#xA;  .use(createFetchPlugin({ client: api }))&#xA;  .mount(&#39;#app&#39;)حالا در component میتوانیم همان client را دریافت کنیم:&lt;script setup lang=&#34;ts&#34;&gt;&#xA;const api = useFetch()&#xA;&#xA;const users = await api.get&lt;User[]&gt;(&#39;/users&#39;)&#xA;در این مدل، component مجبور نیست خودش client را بسازد یا configuration مربوط به authentication و base URL را بداند.SSE در Vueدر نسخه 1.5.0، استفاده از SSE نیز با helper مخصوص Vue سادهتر شده است.مثلاً:&lt;script setup lang=&#34;ts&#34;&gt;&#xA;import { createFetch } from &#39;tanstack-fetch/sse&#39;&#xA;import { useSse } from &#39;tanstack-fetch/vue&#39;&#xA;&#xA;const api = createFetch({&#xA;  plugins: [&#39;sse-resume&#39;],&#xA;})&#xA;&#xA;const { data, status, close } = useSse(&#39;/orders/stream&#39;, {&#xA;  client: api,&#xA;})&#xA;و وضعیت stream از طریق status در دسترس است:&#39;connecting&#39;&#xA;| &#39;connected&#39;&#xA;| &#39;disconnected&#39;&#xA;| &#39;error&#39;این یعنی component میتواند بدون پیادهسازی مستقیم lifecycle مربوط به EventSource، وضعیت connection را مدیریت کند.استفاده در Nuxtبرای Nuxt میتوانیم client را به عنوان یک plugin به application اضافه کنیم.برای مثال:// plugins/tanstack-fetch.ts&#xA;&#xA;import { createFetch } from &#39;tanstack-fetch/sse&#39;&#xA;import { createFetchPlugin } from &#39;tanstack-fetch/vue&#39;&#xA;&#xA;export default defineNuxtPlugin((nuxtApp) =&gt; {&#xA;  const api = createFetch({&#xA;    baseUrl: useRuntimeConfig().public.apiBase,&#xA;    plugins: [&#39;sse-resume&#39;],&#xA;  })&#xA;&#xA;  nuxtApp.vueApp.use(&#xA;    createFetchPlugin({&#xA;      client: api,&#xA;    }),&#xA;  )&#xA;})بعد از آن، componentهای Vue/Nuxt میتوانند از همان client مشترک استفاده کنند.چرا این معماری؟یکی از اهداف اصلی tanstack-fetch این است که HTTP client و server-state management را از هم جدا نگه دارد.برای مثال، tanstack-fetch مسئول مواردی مثل:HTTP requestauthenticationtyped responseFetchErrorAbortSignalinterceptor/pluginSSEاست.در مقابل، ابزارهایی مثل TanStack Query میتوانند مسئول:cachingretriesinvalidationstale statemutationssynchronizationباشند.در نتیجه میتوان چیزی شبیه این داشت:const query = useQuery({&#xA;  queryKey: [&#39;users&#39;],&#xA;  queryFn: ({ signal }) =&gt;&#xA;    api.get&lt;User[]&gt;(&#39;/users&#39;, { signal }),&#xA;})HTTP client فقط request را انجام میدهد و Query مسئول lifecycle مربوط به server state باقی میماند.یک HTTP Client کوچک، نه یک Framework جدیدیکی از تصمیمهای مهم در tanstack-fetch این است که قابلیتهای مختلف تا حد ممکن جدا نگه داشته شوند.برای مثال:import { createFetch } from &#39;tanstack-fetch&#39;برای استفاده معمولی کافی است.اگر SSE لازم داشته باشید:import { createFetch } from &#39;tanstack-fetch/sse&#39;و برای DevTools یا integrationهای دیگر نیز entry pointهای جداگانه وجود دارند.این رویکرد باعث میشود application مجبور نباشد قابلیتهایی را که استفاده نمیکند، به bundle اصلی خود اضافه کند.از FetchError تا AbortSignalمدل خطا نیز بخش مهمی از طراحی tanstack-fetch است.در صورت دریافت HTTP error، به جای اینکه application مجبور باشد هر بار response را بررسی کند، client یک FetchError پرتاب میکند.از طرف دیگر، AbortSignal مستقیماً به request منتقل میشود؛ بنابراین میتوان آن را با lifecycle ابزارهایی مثل TanStack Query هماهنگ کرد.این همان جایی است که مدل ذهنی tanstack-fetch و TanStack Query به خوبی کنار یکدیگر قرار میگیرند.نصببرای نصب آخرین نسخه:npm install tanstack-fetchیا:pnpm add tanstack-fetchلینکهاراهنمای Vue و Nuxthttps://mohamadgarmabi.github.io/tanstack-fetch/guide/vueپست رسمی انتشار نسخه 1.5.0https://mohamadgarmabi.github.io/tanstack-fetch/blog/tanstack-fetch-1-5npmhttps://www.npmjs.com/package/tanstack-fetchGitHubhttps://github.com/mohamadgarmabi/tanstack-fetchجمعبندینسخه 1.5.0 یک قدم مهم برای گسترش tanstack-fetch به خارج از اکوسیستم React است.با اضافه شدن integrationهای Vue 3 و Nuxt، حالا میتوان همان client را با یک الگوی مشابه در پروژههای Vue استفاده کرد؛ در حالی که هستهی HTTP همچنان کوچک و مستقل باقی میماند.اگر در پروژه Vue یا Nuxt خودتان به یک HTTP client typed و سبک نیاز دارید که بتواند در کنار TanStack Query کار کند، میتوانید tanstack-fetch را بررسی کنید.tanstack-fetch یک پروژه مستقل و غیررسمی نسبت به TanStack است.</description><category>garmabi.mohammad73</category><author>garmabi.mohammad73</author><pubDate>Thu, 01 Oct 2026 17:30:55 +0330</pubDate></item><item><title>See Every Fetch؛ دیباگ HTTP، SSE، SSR و tRPC با tanstack-fetch</title><link>https://virgool.ir/@garmabi.mohammad73/see-every-fetch-دیباگ-http-sse-ssr-و-trpc-با-tanstack-fetch-ijimslkcbwnb</link><description>See Every Fetch؛ دیباگ HTTP، SSE، SSR و tRPC با tanstack-fetchدر نسخه 1.4.0 پکیج tanstack-fetch یک DevTools جدید اضافه شده که هدفش خیلی ساده است:به‌جای اینکه فقط ببینیم یک Request چه اتفاقی برایش افتاده، بفهمیم چه بخشی از کد ما آن را ایجاد کرده است.وقتی یک درخواست در Network Tab خراب می‌شود، معمولاً این اطلاعات را داریم:GET /api/profile&#xA;500اما سؤال‌های مهم‌تر معمولاً این‌ها هستند:کدام Hook یا Component این درخواست را ایجاد کرده؟درخواست از SSR آمده یا Browser؟آیا Request دوباره Retry شده؟آیا SSE هنوز باز است یا واقعاً تمام شده؟آیا tRPC از مسیر Interceptorهای ما عبور کرده؟دقیقاً کدام فایل باعث این درخواست شده است؟برای همین در نسخه 1.4.0، tanstack-fetch/devtools اضافه شده است.tanstack-fetch چیست؟tanstack-fetch یک Typed Fetch Client است که API آن برای استفاده در کنار TanStack Query طراحی شده است.قرارداد اصلی آن ساده است:در حالت موفق، data را برمی‌گرداند.در HTTP Error یک FetchError تایپ‌شده throw می‌کند.از AbortSignal پشتیبانی می‌کند.در کنار هسته اصلی، ماژول‌های جداگانه‌ای برای مواردی مثل این‌ها دارد:SSRSSEUploadReacttRPCDevToolsاین پروژه پکیج رسمی TanStack نیست؛ فقط API آن طوری طراحی شده که در کنار TanStack Query طبیعی باشد.نصبnpm i tanstack-fetch@1.4.0&#xA;سپس:import { createFetch } from &#39;tanstack-fetch&#39;&#xA;import { setupDevtools } from &#39;tanstack-fetch/devtools&#39;&#xA;&#xA;const api = createFetch({&#xA;  baseUrl: import.meta.env.VITE_API_URL,&#xA;  plugins: [&#39;trace&#39;],&#xA;})&#xA;&#xA;if (import.meta.env.DEV) {&#xA;  setupDevtools(api)&#xA;}&#xA;همین.DevTools یک Interceptor ثبت می‌کند و یک Dock در پایین صفحه قرار می‌دهد.پیشنهاد می‌کنم trace را هم فعال کنید تا هر Request یک شناسه پایدار داشته باشد:requestId:attempt&#xA;این موضوع برای Requestهایی که Retry می‌شوند بسیار کاربردی است.باز کردن DevToolsمی‌توانید با این Shortcut آن را باز و بسته کنید:Alt + Shift + Fدر macOS:⌥ ⇧ Fچه چیزهایی را می‌توان دید؟HTTPبرای هر Request اطلاعاتی مثل این‌ها نمایش داده می‌شود:Method&#xA;Path&#xA;Status&#xA;Duration&#xA;Attempts&#xA;Call Site&#xA;مثلاً:GET /api/profile&#xA;200&#xA;143ms&#xA;profile.hook.ts&#xA;یعنی علاوه بر اطلاعات Network، می‌توانیم بفهمیم Request از کجای Application آمده است.SSEبرای SSE وضعیت Stream اهمیت زیادی دارد.در نسخه 1.4.0 وضعیت می‌تواند به شکل زیر باشد:pending → live → success&#xA;یا در صورت خطا:pending → live → error&#xA;و در صورت Cancel شدن:pending → live → aborted&#xA;همچنین Eventهای دریافتی SSE قابل مشاهده هستند:id&#xA;name&#xA;payload&#xA;در نتیجه برای Debug کردن Stream لازم نیست یک Logger جداگانه در console بنویسید.SSRDevTools می‌تواند Requestهای SSR و Edge را از Requestهای معمولی Browser تشخیص دهد.یکی از کاربردهای مهمش بررسی Forward شدن Cookie و Authentication Context است، بدون اینکه Secretها را در DevTools نمایش دهد.tRPCبرای tRPC نیز Requestها در همان جریان DevTools ثبت می‌شوند.در Adapter مربوط به tRPC از:meta.operation = &#39;trpc&#39;&#xA;برای تشخیص نوع Operation استفاده می‌شود.Call Graph؛ قسمت مهم ماجرایکی از بخش‌هایی که برای من در این DevTools مهم بود، پیدا کردن Source Request است.فرض کنید فقط این را می‌بینیم:GET /api/profile&#xA;500&#xA;DevTools می‌تواند مسیر Application را هم نشان دهد:ProfilePage&#xA;    ↓&#xA;useProfile&#xA;    ↓&#xA;profile.hook.ts&#xA;    ↓&#xA;api.get()&#xA;بعد می‌توان روی Node مربوط به Source کلیک کرد و فایل را مستقیماً در:CursorZedVS Codeباز کرد.یعنی مسیر Debug می‌شود:Failed Request&#xA;      ↓&#xA;DevTools&#xA;      ↓&#xA;Call Graph&#xA;      ↓&#xA;Source File&#xA;      ↓&#xA;Editor&#xA;این چیزی است که Network Tab به‌تنهایی نمی‌تواند به شما بدهد.چرا فقط Network Tab کافی نیست؟Network Tab مرورگر برای بررسی اتفاقات روی Wire عالی است.اما این DevTools به Pipeline خود createFetch متصل است.نوعتشخیصHTTPFetch معمولیSSRsource: &#39;ssr&#39; | &#39;edge&#39; یا Incoming AuthSSEonSseOpen / Events / onSseClosetRPCmeta.operation = &#39;trpc&#39;در نتیجه یک Dock می‌تواند Requestهایی را که از مسیرهای مختلف آمده‌اند کنار هم نشان دهد:React Query&#xA;    ↓&#xA;createFetch&#xA;    ↓&#xA;Interceptor Pipeline&#xA;    ↓&#xA;HTTP / SSE / SSR / tRPC&#xA;    ↓&#xA;DevTools&#xA;دو تغییر مهم در نسخه 1.4.0مشکل tRPCقبل از نسخه 1.4.0، Response موفق tRPC می‌توانست onResponse را رد کند.در نتیجه ابزارهای Logging و DevTools همیشه نمی‌توانستند Timing مربوط به Request موفق را مشاهده کنند.در نسخه جدید، Adapter برای Response موفق onResponse را اجرا می‌کند و Body همچنان برای Parser خود tRPC unread باقی می‌ماند.مشکل SSE PendingStreamهای SSE رفتار متفاوتی نسبت به HTTP معمولی دارند.قبلاً Lifecycle مشخصی برای پایان Stream وجود نداشت.در نسخه 1.4.0، onSseClose اضافه شده است.بنابراین وقتی Stream به شکل صحیح بسته شود:live&#xA; ↓&#xA;onSseClose&#xA; ↓&#xA;success&#xA;و اگر خطا یا Abort اتفاق بیفتد، وضعیت مناسب ثبت می‌شود.استفاده از یک Store برای چند Clientاگر HTTP و SSE را با Factoryهای جدا ساخته باشید، باز هم می‌توانید یک Store مشترک داشته باشید:import { createFetch } from &#39;tanstack-fetch/sse&#39;&#xA;import {&#xA;  createDevtools,&#xA;  mountDevtools,&#xA;} from &#39;tanstack-fetch/devtools&#39;&#xA;&#xA;const dt = createDevtools({&#xA;  http: true,&#xA;  sse: true,&#xA;  ssr: true,&#xA;  trpc: true,&#xA;})&#xA;&#xA;const api = createFetch({&#xA;  plugins: [&#39;trace&#39;, &#39;sse-resume&#39;],&#xA;  interceptors: [dt.interceptor],&#xA;})&#xA;&#xA;mountDevtools({&#xA;  store: dt.store,&#xA;  open: true,&#xA;})&#xA;در این حالت همه Requestها در یک Dock دیده می‌شوند.نسخه Live را امتحان کنیدبرای DevTools یک Demo آنلاین هم آماده کرده‌ام که بدون نصب می‌توانید امتحانش کنید:DevTools Exampleمی‌توانید HTTP، Upload، SSR، SSE و tRPC را اجرا کنید و بعد با:Alt + Shift + FDevTools را باز کنید.چه زمانی کاربردی است؟این ابزار بیشتر برای Development و Debugging طراحی شده است.مثلاً:TanStack Queryبرای پیدا کردن Requestهای تکراری، Retryها و Raceهای احتمالی.SSR Authenticationبرای بررسی اینکه Cookie و Authentication Context درست Forward شده‌اند.SSEبرای دیدن Eventهای Stream و Lifecycle آن.Debug کردن Sourceبه‌جای اینکه فقط بدانیم:500 POST /api/orders&#xA;بتوانیم مستقیم به چیزی مثل این برسیم:createOrder.hook.ts:42&#xA;در Production فعالش نکنیدبهتر است DevTools را فقط در محیط Development فعال کنید:if (import.meta.env.DEV) {&#xA;  setupDevtools(api)&#xA;}&#xA;لینک‌هاnpm — tanstack-fetchDocumentationChangelog 1.4.0Live DevTools DemoGitHubاگر در یک پروژه واقعی از DevTools استفاده کردید، خوشحال می‌شوم بدانم در نسخه‌های بعدی چه چیزی اضافه شود؛ مثلاً Filter، Export، React Wrapper یا قابلیت دیگری.</description><category>garmabi.mohammad73</category><author>garmabi.mohammad73</author><pubDate>Sun, 27 Sep 2026 13:19:33 +0330</pubDate></item><item><title>tanstack-fetch؛ یک Fetch Client تایپ‌شده برای TanStack Query</title><link>https://virgool.ir/@garmabi.mohammad73/tanstack-fetch-یک-fetch-client-تایپ-شده-برای-tanstack-query-znhsz3kiyt6i</link><description>وقتی با React و TanStack Query کار می‌کنیم، بخش زیادی از کدهای پروژه در نهایت به API request ختم می‌شوند.چیزی شبیه این:const { data } = useQuery({&#xA;  queryKey: [&#39;users&#39;],&#xA;  queryFn: ({ signal }) =&gt;&#xA;    fetch(&#39;/api/users&#39;, {&#xA;      signal,&#xA;    }).then((res) =&gt; res.json()),&#xA;})در نگاه اول ساده است.اما با بزرگ‌تر شدن پروژه، خیلی سریع به این مشکلات می‌رسیم:مدیریت خطاTypeScriptAuthenticationRefresh TokenRetryAbortSignalSSRSSEUploadInterceptorsPath ParametersError Handlingتفاوت رفتار GET و Mutationو در نهایت یک مجموعه wrapper دور fetchاینجاست که tanstack-fetch شکل گرفت.tanstack-fetch چیست؟tanstack-fetch یک typed Fetch client است که API آن با نحوه‌ی استفاده از TanStack Query هماهنگ شده است.هدف پروژه این نیست که یک Axios دیگر بسازد.ایده ساده‌تر است:اگر TanStack Query یک لایه برای مدیریت server state است، tanstack-fetch باید یک لایه‌ی کوچک و قابل پیش‌بینی برای HTTP باشد.مدل ذهنی پروژه بسیار ساده است:TanStack Query&#xA;       │&#xA;       ▼&#xA; tanstack-fetch&#xA;       │&#xA;       ▼&#xA;    fetch()&#xA;       │&#xA;       ▼&#xA;      APIچرا یک Fetch Client جدید؟خود fetch عالی است.اما API خام آن چند مشکل دارد.مثلاً:const response = await fetch(&#39;/users&#39;)&#xA;&#xA;if (!response.ok) {&#xA;  throw new Error(&#39;Request failed&#39;)&#xA;}&#xA;&#xA;const data = await response.json()تقریباً در هر پروژه‌ای دوباره همین کد را می‌نویسیم.حالا Authentication را اضافه کنیم:const response = await fetch(&#39;/users&#39;, {&#xA;  headers: {&#xA;    Authorization: `Bearer ${token}`,&#xA;  },&#xA;})بعد:401&#xA; ↓&#xA;refresh token&#xA; ↓&#xA;retryبعد:429&#xA; ↓&#xA;Retry-After&#xA; ↓&#xA;wait&#xA; ↓&#xA;retryبعد:AbortSignal&#xA;SSR&#xA;SSE&#xA;Upload&#xA;Interceptorsدر نهایت یک HTTP abstraction ساخته‌ایم؛ فقط بدون اینکه از ابتدا طراحی شده باشد.tanstack-fetch تلاش می‌کند این لایه را کوچک، typed و قابل ترکیب نگه دارد.فلسفه‌ی اصلیtanstack-fetch روی چند اصل ساده ساخته شده است.1. Data in, data outدر موفقیت، چیزی که می‌خواهیم معمولاً خود data است:const user = await api.get&lt;User&gt;(&#39;/users/1&#39;)نه:const response = await api.get(&#39;/users/1&#39;)&#xA;&#xA;response.data2. HTTP error باید واقعاً Error باشداگر API پاسخ 4xx یا 5xx بدهد، request نباید مثل یک موفقیت رفتار کند.tanstack-fetch از FetchError استفاده می‌کند:try {&#xA;  await api.get(&#39;/users&#39;)&#xA;} catch (error) {&#xA;  if (error instanceof FetchError) {&#xA;    console.log(error.status)&#xA;  }&#xA;}در نتیجه error handling در TanStack Query هم طبیعی باقی می‌ماند.3. AbortSignal از ابتدا first-class استTanStack Query هنگام cancellation، signal در اختیار queryFn قرار می‌دهد:useQuery({&#xA;  queryKey: [&#39;users&#39;],&#xA;  queryFn: ({ signal }) =&gt;&#xA;    api.get(&#39;/users&#39;, {&#xA;      signal,&#xA;    }),&#xA;})tanstack-fetch این signal را به fetch منتقل می‌کند.پس cancellation توسط Query و HTTP client با هم کار می‌کنند.TypeScript Firstیکی از مهم‌ترین اهداف tanstack-fetch این است که TypeScript فقط تزئینی نباشد.مثلاً:type User = {&#xA;  id: string&#xA;  name: string&#xA;  email: string&#xA;}&#xA;&#xA;const user = await api.get&lt;User&gt;(&#39;/users/123&#39;)حالا:user.name&#xA;user.emailکاملاً typed هستند.Path Parametersیکی از قابلیت‌های جالب نسخه‌های اخیر، typed path parameters است.مثلاً:api.get(&#39;/users/:id&#39;, {&#xA;  params: {&#xA;    id: userId,&#xA;  },&#xA;})TypeScript از مسیر متوجه می‌شود که id لازم است.برای مثال:api.get(&#39;/users/:id&#39;, {&#xA;  params: {},&#xA;})می‌تواند به عنوان استفاده‌ی نادرست شناسایی شود.این قابلیت برای APIهای بزرگ که endpointهای زیادی دارند، جلوی بخشی از خطاهای runtime را می‌گیرد.TanStack Query Integrationیکی از مهم‌ترین تفاوت‌های tanstack-fetch این است که API آن برای استفاده مستقیم در queryFn طراحی شده است.const api = createFetch({&#xA;  baseUrl: &#39;https://api.example.com&#39;,&#xA;})&#xA;&#xA;const usersQuery = useQuery({&#xA;  queryKey: [&#39;users&#39;],&#xA;  queryFn: ({ signal }) =&gt;&#xA;    api.get&lt;User[]&gt;(&#39;/users&#39;, {&#xA;      signal,&#xA;    }),&#xA;})هیچ adapter خاصی لازم نیست.نه:response.dataو نه:queryFn: async () =&gt; {&#xA;  const response = await axios.get(...)&#xA;  return response.data&#xA;}فقط:api.get(...)AuthenticationAuthentication یکی از قسمت‌هایی است که HTTP client باید بتواند آن را مدیریت کند.مثلاً:const api = createFetch({&#xA;  baseUrl: API_URL,&#xA;&#xA;  getToken: () =&gt; accessToken,&#xA;})درخواست‌ها می‌توانند به شکل متمرکز token دریافت کنند.در نتیجه لازم نیست در هر request بنویسیم:headers: {&#xA;  Authorization: `Bearer ${token}`,&#xA;}Refresh Tokenدر نسخه 1.3.0 قابلیت مهمی اضافه شده است:createRefreshTokenInterceptorاین interceptor می‌تواند دو سناریو را مدیریت کند.قبل از expiryاگر token نزدیک expiration باشد:Token&#xA;│&#xA;├───────────────┬───────────────┤&#xA;                ↑&#xA;          refresh thresholdقبل از request، token refresh می‌شود.بعد از 401اگر token هنوز از دید client معتبر باشد اما server پاسخ بدهد:401 Unauthorizedinterceptor می‌تواند:401&#xA; ↓&#xA;refresh token&#xA; ↓&#xA;retry original requestرا انجام دهد.Single-Flight Refreshاما قسمت مهم‌تر اینجاست.فرض کنید ۱۰ Query هم‌زمان 401 دریافت کنند.پیاده‌سازی ساده ممکن است این کار را انجام دهد:Request 1 → refresh&#xA;Request 2 → refresh&#xA;Request 3 → refresh&#xA;Request 4 → refresh&#xA;...&#xA;Request 10 → refreshکه اصلاً مطلوب نیست.tanstack-fetch می‌تواند refresh را به صورت single-flight هماهنگ کند:Request 1 ──┐&#xA;Request 2 ──┤&#xA;Request 3 ──┤&#xA;Request 4 ──┤&#xA;Request 5 ──┼──→ ONE refresh&#xA;Request 6 ──┤&#xA;Request 7 ──┤&#xA;Request 8 ──┤&#xA;Request 9 ──┤&#xA;Request 10 ─┘&#xA;                 │&#xA;                 ▼&#xA;             retry allیعنی چند request منتظر همان refresh operation می‌مانند.این موضوع برای applicationهای concurrent اهمیت زیادی دارد.Interceptorstanstack-fetch یک سیستم interceptor دارد که اجازه می‌دهد رفتار request را بدون آلوده کردن feature code تغییر دهیم.به جای اینکه در تک‌تک Queryها بنویسیم:if (response.status === 401) {&#xA;  ...&#xA;}می‌توانیم رفتار را در HTTP layer قرار دهیم.ساختار ذهنی:Request&#xA;   │&#xA;   ▼&#xA;Interceptor&#xA;   │&#xA;   ▼&#xA;fetch&#xA;   │&#xA;   ▼&#xA;Response&#xA;   │&#xA;   ▼&#xA;Interceptor&#xA;   │&#xA;   ▼&#xA;Applicationاین separation برای پروژه‌های بزرگ‌تر بسیار مهم است.HTTP Error Handlingدر پروژه‌های واقعی همه‌ی errorها یکسان نیستند.مثلاً:401 → Authentication&#xA;403 → Authorization&#xA;404 → Resource not found&#xA;429 → Rate limiting&#xA;4xx → Client error&#xA;5xx → Server errortanstack-fetch برای این سناریوها handlerهایی ارائه می‌کند.مثلاً:createFetch({&#xA;  onUnauthorized: () =&gt; {&#xA;    // logout&#xA;  },&#xA;&#xA;  onTooManyRequests: (error) =&gt; {&#xA;    // handle rate limit&#xA;  },&#xA;&#xA;  onClientError: (error) =&gt; {&#xA;    // common 4xx handling&#xA;  },&#xA;})این باعث می‌شود policy مربوط به HTTP در یک محل مشخص قرار بگیرد.Rate Limitingیکی دیگر از قابلیت‌ها، کار با Retry-After است.وقتی server می‌گوید:429 Too Many Requests&#xA;Retry-After: 10client می‌تواند بفهمد که چه مدت باید صبر کند.وجود parseRetryAfter باعث می‌شود این رفتار در HTTP layer قابل مدیریت باشد.این موضوع مخصوصاً برای APIهایی که rate limit دارند اهمیت دارد.RetryRetry باید با دقت انجام شود.برای همه‌ی requestها retry کردن لزوماً درست نیست.مثلاً retry کردن یک GET معمولاً با retry کردن یک mutation یکسان نیست.HTTP client باید اطلاعات کافی را در اختیار application قرار دهد تا retry policy قابل کنترل باشد.این موضوع در کنار TanStack Query اهمیت بیشتری پیدا می‌کند، چون خود Query نیز مکانیزم retry دارد.بنابراین بهتر است مسئولیت‌ها مشخص باشند:TanStack Query&#xA;    │&#xA;    ├── Query retry&#xA;    ├── Cache&#xA;    ├── Refetch&#xA;    └── Server state&#xA;          │&#xA;          ▼&#xA;    tanstack-fetch&#xA;          │&#xA;          ├── HTTP&#xA;          ├── Auth&#xA;          ├── Interceptors&#xA;          └── Transport errorsSSR Supporttanstack-fetch فقط برای browser طراحی نشده است.در applicationهایی که SSR دارند، استفاده از abstractionی که بیش از حد به:window&#xA;localStorage&#xA;documentوابسته باشد، مشکل‌ساز می‌شود.به همین دلیل HTTP core پروژه تلاش می‌کند environment-agnostic باقی بماند.این موضوع استفاده از آن را در معماری‌هایی مثل:Next.js&#xA;Remix&#xA;Node.js&#xA;Edgeساده‌تر می‌کند.SSESSE یا Server-Sent Events برای دریافت stream از server استفاده می‌شود.مثلاً:Server&#xA;  │&#xA;  ├── event&#xA;  ├── event&#xA;  ├── event&#xA;  ├── event&#xA;  └── event&#xA;       ↓&#xA;     Clienttanstack-fetch برای SSE نیز قابلیت‌های مربوط به streaming را ارائه می‌کند.این موضوع برای:live updatesnotificationsdashboardsAI responsesrealtime-ish applicationsکاربرد دارد.SSE Resumeدر سیستم‌های streaming، قطع شدن connection مسئله‌ی مهمی است.اگر client وسط stream disconnect شود، ideally نباید همیشه از ابتدا شروع کند.پشتیبانی از resume در SSE می‌تواند کمک کند client از نقطه‌ی قبلی ادامه دهد.این قابلیت مخصوصاً برای streamهای طولانی‌تر اهمیت دارد.UploadUpload نیز یکی از قابلیت‌های optional در ecosystem پروژه است.در applicationهای واقعی ممکن است نیاز داشته باشیم:JSON&#xA;FormData&#xA;File&#xA;Blob&#xA;Multipartرا ارسال کنیم.داشتن API یکپارچه برای transport باعث می‌شود feature code کمتر درگیر جزئیات fetch شود.tRPCtanstack-fetch فقط محدود به REST نیست.برای tRPC نیز entry point جداگانه در نظر گرفته شده است.ایده این است که transport layer بتواند در معماری‌های مختلف استفاده شود، بدون اینکه HTTP core مجبور شود تمام قابلیت‌ها را به صورت monolithic داخل خودش قرار دهد.React Helpersدر کنار HTTP core، helperهایی برای React نیز وجود دارند.اما یک تصمیم معماری مهم این است که همه‌چیز در core قرار نگیرد.به جای یک package بسیار بزرگ:tanstack-fetch&#xA;├── React&#xA;├── SSE&#xA;├── Upload&#xA;├── SSR&#xA;├── tRPC&#xA;└── ...قابلیت‌ها تا حد امکان به صورت entry pointهای جداگانه در دسترس هستند.این به کاهش حجم bundle و ساده‌تر شدن dependency graph کمک می‌کند.حجم Coreیکی از اهداف پروژه کوچک نگه داشتن HTTP core است.HTTP core تقریباً 3.5 KB gzip است.یعنی اگر فقط یک Fetch client ساده می‌خواهید، مجبور نیستید تمام قابلیت‌های ecosystem را وارد bundle کنید.API سادهدر ساده‌ترین حالت:import { createFetch } from &#39;tanstack-fetch&#39;&#xA;&#xA;const api = createFetch({&#xA;  baseUrl: &#39;https://api.example.com&#39;,&#xA;})&#xA;&#xA;const users = await api.get&lt;User[]&gt;(&#39;/users&#39;)POST:const user = await api.post&lt;User&gt;(&#39;/users&#39;, {&#xA;  body: {&#xA;    name: &#39;Mohammad&#39;,&#xA;    email: &#39;mohammad@example.com&#39;,&#xA;  },&#xA;})PUT:await api.put(&#39;/users/123&#39;, {&#xA;  body: {&#xA;    name: &#39;Mohammad&#39;,&#xA;  },&#xA;})DELETE:await api.delete(&#39;/users/123&#39;)و در تمام این موارد TypeScript می‌تواند type response را مدیریت کند.AbortSignalیکی از مهم‌ترین integrationها با TanStack Query:useQuery({&#xA;  queryKey: [&#39;users&#39;],&#xA;  queryFn: ({ signal }) =&gt;&#xA;    api.get(&#39;/users&#39;, {&#xA;      signal,&#xA;    }),&#xA;})وقتی Query عملیات را cancel کند، AbortSignal به request منتقل می‌شود.یعنی:TanStack Query&#xA;      │&#xA;      │ AbortSignal&#xA;      ▼&#xA;tanstack-fetch&#xA;      │&#xA;      ▼&#xA;fetch()&#xA;      │&#xA;      ▼&#xA;APIاین integration کوچک است، اما در applicationهای بزرگ تفاوت قابل توجهی ایجاد می‌کند.چرا Axios نه؟هدف tanstack-fetch این نیست که بگوید Axios بد است.Axios یک HTTP client mature و بسیار پرکاربرد است.اما اگر پروژه‌ای از:React&#xA;+&#xA;TanStack Query&#xA;+&#xA;TypeScript&#xA;+&#xA;native Fetchاستفاده می‌کند، ممکن است یک abstraction سبک‌تر و نزدیک‌تر به Fetch مناسب‌تر باشد.مقایسه‌ی mental model:Axios&#xA;&#xA;Query&#xA;  ↓&#xA;Axios adapter&#xA;  ↓&#xA;Axios response&#xA;  ↓&#xA;response.dataدر tanstack-fetch:Query&#xA;  ↓&#xA;tanstack-fetch&#xA;  ↓&#xA;dataهر دو رویکرد معتبرند؛ مسئله این است که abstraction باید با architecture پروژه هماهنگ باشد.چرا این پروژه برای TanStack Query ساخته شده؟TanStack Query خودش HTTP client نیست.وظیفه‌ی آن چیزهایی مثل:cachingsynchronizationinvalidationrefetchingstale statemutationsserver stateاست.اما برای اجرای request باید یک queryFn داشته باشیم.اینجا tanstack-fetch وارد می‌شود.             Application&#xA;                  │&#xA;                  ▼&#xA;          TanStack Query&#xA;                  │&#xA;                  ▼&#xA;           tanstack-fetch&#xA;                  │&#xA;                  ▼&#xA;               Fetch&#xA;                  │&#xA;                  ▼&#xA;                APIهر لایه یک مسئولیت مشخص دارد.چه زمانی tanstack-fetch مناسب است؟اگر پروژه‌ی شما این stack را دارد:React&#xA;TypeScript&#xA;TanStack Query&#xA;REST API&#xA;Fetchtanstack-fetch می‌تواند یک گزینه باشد.خصوصاً اگر نیاز دارید:API client typed داشته باشیدerror handling متمرکز داشته باشیدAuthentication را متمرکز کنیدRefresh Token داشته باشیدrequest cancellation داشته باشیدpath parameters typed باشندSSR داشته باشیدSSE استفاده کنیدinterceptor داشته باشیدبدون Axios adapter با TanStack Query کار کنیدچه زمانی شاید به آن نیاز نداشته باشید؟اگر پروژه فقط چند request ساده دارد:fetch(&#39;/api/users&#39;)&#xA;fetch(&#39;/api/posts&#39;)احتمالاً خود fetch کافی است.هدف tanstack-fetch اضافه کردن abstraction صرفاً برای abstraction نیست.وقتی complexity ایجاد می‌شود، abstraction باید complexity را کم کند، نه اینکه خودش تبدیل به complexity جدید شود.معماری کلیاگر بخواهیم کل قابلیت‌ها را در یک تصویر ذهنی قرار دهیم:                         Application&#xA;                              │&#xA;                              ▼&#xA;                       TanStack Query&#xA;                              │&#xA;                    ┌─────────┴─────────┐&#xA;                    │                   │&#xA;                    ▼                   ▼&#xA;              Query / Mutation       React&#xA;                    │&#xA;                    ▼&#xA;               tanstack-fetch&#xA;                    │&#xA;        ┌───────────┼───────────┐&#xA;        │           │           │&#xA;        ▼           ▼           ▼&#xA;      Auth       Interceptors  Errors&#xA;        │           │           │&#xA;        └───────────┼───────────┘&#xA;                    │&#xA;          ┌─────────┼─────────┐&#xA;          │         │         │&#xA;          ▼         ▼         ▼&#xA;        Fetch      SSE      Upload&#xA;          │&#xA;          ▼&#xA;         APIنصبnpm install tanstack-fetchاگر از TanStack Query استفاده می‌کنید:npm install tanstack-fetch @tanstack/react-queryیک مثال واقعییک application فرضی داریم:React&#xA;   │&#xA;   ├── Users&#xA;   ├── Orders&#xA;   ├── Dashboard&#xA;   └── Notifications&#xA;          │&#xA;          ▼&#xA;    TanStack Query&#xA;          │&#xA;          ▼&#xA;    tanstack-fetch&#xA;          │&#xA;          ├── Authentication&#xA;          ├── Refresh Token&#xA;          ├── Error Handling&#xA;          ├── Retry&#xA;          └── SSE&#xA;          │&#xA;          ▼&#xA;        Backendدر featureهای مختلف دیگر لازم نیست این منطق تکرار شود.مثلاً:export const getUsers = (signal?: AbortSignal) =&gt;&#xA;  api.get&lt;User[]&gt;(&#39;/users&#39;, {&#xA;    signal,&#xA;  })و:export const getOrders = (signal?: AbortSignal) =&gt;&#xA;  api.get&lt;Order[]&gt;(&#39;/orders&#39;, {&#xA;    signal,&#xA;  })سپس:useQuery({&#xA;  queryKey: [&#39;users&#39;],&#xA;  queryFn: ({ signal }) =&gt; getUsers(signal),&#xA;})کد feature ساده باقی می‌ماند.نسخه 1.3.0در نسخه 1.3.0 تمرکز اصلی روی authentication و refresh token بوده است.مهم‌ترین قابلیت:createRefreshTokenInterceptor()با دو مسیر:BEFORE&#xA;Token نزدیک expiry&#xA;      ↓&#xA;   refresh&#xA;      ↓&#xA;   requestو:AFTER&#xA;401&#xA; ↓&#xA;refresh&#xA; ↓&#xA;retryبه همراه:Single-flightیعنی چند request هم‌زمان باعث چند refresh request نمی‌شوند.مسیر پروژهtanstack-fetch یک پروژه‌ی Open Source است و هدفش این است که یک abstraction کوچک و composable برای Fetch و TanStack Query ارائه کند.چند اصل پروژه برای من مهم هستند:Simple over clever.Typed over implicit.Composable over monolithic.و مهم‌تر از همه:یک abstraction خوب باید پیچیدگی را از application بگیرد، نه اینکه پیچیدگی جدیدی به آن اضافه کند.نتیجهfetch به تنهایی ابزار قدرتمندی است.TanStack Query هم مدیریت server state را بسیار خوب انجام می‌دهد.اما بین این دو یک فضای کوچک وجود دارد:Query&#xA;  ↓&#xA;HTTPtanstack-fetch تلاش می‌کند این فاصله را پر کند.نه با یک framework بزرگ.نه با یک abstraction پیچیده.بلکه با یک Fetch client typed و قابل ترکیب که برای سناریوهای واقعی مثل:AuthenticationRefresh TokenSingle-flightInterceptorsHTTP ErrorsRetryRate LimitingAbortSignalSSRSSEUploadTyped Path Parametersساخته شده است.اگر از TanStack Query استفاده می‌کنید، می‌توانید tanstack-fetch را امتحان کنید و ببینید آیا این مدل برای architecture پروژه‌ی شما مناسب است یا نه.LinksnpmDocumentationGitHubRefresh Token RecipeChangelogtanstack-fetch — Typed Fetch for TanStack Query.</description><category>garmabi.mohammad73</category><author>garmabi.mohammad73</author><pubDate>Fri, 25 Sep 2026 14:13:36 +0330</pubDate></item></channel></rss>