توضیحات مقاله: در این آموزش یاد میگیریم چطور با tanstack-fetch، Angular Dependency Injection و TanStack Query یک لایهی API قابلاستفادهی مجدد، تایپسیف و پشتیبان از لغو درخواست بسازیم.
در پروژههای Angular معمولاً کار با API با چند درخواست ساده شروع میشود؛ اما با بزرگتر شدن پروژه، مدیریت درخواستها، خطاها، تایپ پاسخها و لغو درخواستها میتواند به یک چالش تبدیل شود.
اگر هر کامپوننت منطق درخواست HTTP خودش را داشته باشد، نگهداری و یکپارچهسازی رفتارها دشوارتر خواهد شد.
در این مقاله، یک روش برای جداسازی لایهی HTTP از مدیریت وضعیت سرور را با استفاده از tanstack-fetch، Angular و TanStack Query بررسی میکنیم.
tanstack-fetch یک HTTP client مستقل مبتنی بر TypeScript است که برای استفاده در کنار الگوی برنامهنویسی TanStack Query طراحی شده است.
مسئولیت ابزارها را میتوان اینطور تقسیم کرد:
tanstack-fetch: ارسال درخواست HTTP و مدیریت خطاهای مرتبط با کلاینت.
TanStack Query: مدیریت چرخهی عمر query و کش دادههای سمت سرور.
Angular: مدیریت کامپوننتها و Dependency Injection.
این پروژه توسط Mohammad Garmabi توسعه داده شده و یک پکیج رسمی از تیم TanStack نیست.
ابتدا پکیجهای موردنیاز را نصب میکنیم:
npm install tanstack-fetch npm install @tanstack/angular-query-experimental
این نمونه بر اساس API ارائهشده در مستندات Angular پروژه نوشته شده است. پیش از استفاده در محیط production، سازگاری نسخههای نصبشده را بررسی کنید.
بهتر است تنظیمات API را در یک محل مشخص نگه داریم:
// api.ts import { createFetch } from 'tanstack-fetch'; export interface User { id: number; name: string; email: string; } export const api = createFetch({ baseUrl: 'https://jsonplaceholder.typicode.com', });
در این مثال از یک API آزمایشی استفاده میکنیم. در پروژهی واقعی میتوانید آدرس API خودتان را قرار دهید.
حالا client را به providerهای برنامه اضافه میکنیم:
import { bootstrapApplication } from '@angular/platform-browser'; import { provideFetchClient } from 'tanstack-fetch/angular'; import { api } from './api'; import { UsersComponent } from './users.component'; bootstrapApplication(UsersComponent, { providers: [ provideFetchClient({ client: api }), ], });
پس از این مرحله، میتوانیم در context معتبر Angular از injectFetch() برای دریافت client استفاده کنیم.
در مثال زیر، فهرست کاربران را دریافت میکنیم و وضعیت درخواست را در رابط کاربری نمایش میدهیم.
import { Component } from '@angular/core'; import { injectQuery } from '@tanstack/angular-query-experimental'; import { injectFetch } from 'tanstack-fetch/angular'; import { isFetchError } from 'tanstack-fetch'; import { User } from './api'; @Component({ selector: 'app-users', standalone: true, template: ` @if (users.isPending()) { <p>در حال دریافت اطلاعات...</p> } @else if (users.isError()) { <p>{{ errorLabel() }}</p> } @else { <ul> @for (user of users.data(); track user.id) { <li>{{ user.name }} - {{ user.email }}</li> } </ul> } `, }) export class UsersComponent { private readonly api = injectFetch(); readonly users = injectQuery(() => ({ queryKey: ['users'], queryFn: ({ signal }: { signal: AbortSignal }) => this.api.get<User[]>('/users', { signal }), })); errorLabel(): string { const error = this.users.error(); return isFetchError(error) ? `خطای HTTP ${error.status}: ${error.message}` : 'دریافت اطلاعات ناموفق بود'; } }
۱. تایپسیف بودن پاسخها
با استفاده از get<User[]> نوع دادهی موردانتظار را مشخص میکنیم. این کار به خوانایی کد و تشخیص خطاهای تایپی کمک میکند؛ البته صحت واقعی دادههای دریافتی از سرور را بهتنهایی تضمین نمیکند.
۲. استفاده از Dependency Injection
بهجای ساختن client در هر کامپوننت، از یک نمونهی پیکربندیشده استفاده میکنیم.
۳. لغو درخواست با AbortSignal
سیگنال دریافتی از TanStack Query را به درخواست HTTP منتقل میکنیم:
queryFn: ({ signal }: { signal: AbortSignal }) => this.api.get<User[]>('/users', { signal })
این اتصال اجازه میدهد درخواست در صورت لغو شدن توسط چرخهی عمر query، عملیات لغو را دریافت کند؛ مشروط به اینکه HTTP client سیگنال را به درخواست زیرین منتقل کند.
۴. جداسازی مدیریت خطا
تابع isFetchError() کمک میکند خطاهای مربوط به HTTP client را از خطاهای دیگر متمایز کنیم و پیام مناسبتری به کاربر نمایش دهیم.
۵. جداسازی HTTP از مدیریت وضعیت سرور
TanStack Query مسئول کش و چرخهی عمر query است، درحالیکه tanstack-fetch درخواست HTTP را انجام میدهد. این تفکیک از ترکیب شدن مسئولیتهای متفاوت در یک کامپوننت جلوگیری میکند.
خیر. Angular ابزار داخلی HttpClient را هم در اختیار توسعهدهندگان قرار میدهد و برای بسیاری از پروژهها کاملاً کافی است.
استفاده از یک client جداگانه زمانی ارزشمند است که به قراردادهای مشترک برای درخواستها، تایپ پاسخها، مدیریت خطا یا یکپارچگی بیشتر با ابزارهای مدیریت server state نیاز داشته باشید.
هدف، پیچیدهتر کردن معماری نیست؛ هدف این است که هر لایه مسئولیت مشخصی داشته باشد.
با ترکیب Angular Dependency Injection، tanstack-fetch و TanStack Query میتوان یک لایهی API ساخت که درخواستهای تایپسیف را با مدیریت وضعیت سرور و امکان انتقال سیگنال لغو هماهنگ میکند.
اگر میخواهید جزئیات API و مثالهای بیشتر را ببینید، مستندات پروژه در دسترس است:
نویسنده: Mohammad Garmabi، توسعهدهندهی نرمافزار و سازندهی پروژهی متنباز tanstack-fetch.