ساخت REST API typeafe با React Query و Zod

من مدت زیادی است که طرفدار استفاده از tRPC برای ساختن APIهای ایمن از نوع انتها به انتها هستم. این یک کتابخانه شگفتانگیز است که به شما امکان میدهد مستقیماً توابع باطن خود را از قسمت جلویی با ایمنی کامل فراخوانی کنید. به طور خودکار انواع را استنباط می کند، بنابراین دیگر نگران تولید کد مانند GraphQL نخواهید بود. علاوه بر این، React Query را بسته بندی می کند. این بدان معناست که شما از تمام مزایای مدیریت ایالتی که React Query ارائه میکند، لذت خواهید برد.
ایراد بزرگ این است که tRPC نه تنها نیاز شما را دارد، بلکه باطن شما را نیز باید در TypeScript نوشته شود. و حتی اگر اینطور باشد، برای استنتاج نوع بین frontend و backend باید همه چیز را در monorepo قرار دهید. در بسیاری از پروژهها، این یک راهانداز نیست، زیرا ممکن است قبلاً یک REST API کاملاً تعریف شده در یک فناوری کاملاً متفاوت داشته باشید. بنابراین بیایید فرض کنیم که با یک REST API نوشته شده در جنگو کار می کنیم که از React frontend خود که در TypeScript نوشته شده است آن را فراخوانی می کنیم. چگونه می توانیم بهترین تجربه توسعه دهنده را داشته باشیم؟
نکته: این یک آموزش کامل نخواهد بود که به شما نشان می دهد چگونه این کتابخانه ها را در پایگاه کد خود پیکربندی کنید. اسناد رسمی می تواند در این زمینه به شما کمک کند. در عوض، این را یک نمای کلی مفهومی از نحوه ترکیب فناوری ها برای بهترین تجربه توسعه دهندگان در نظر بگیرید.
واکشی داده با React Query
React Query که با نام TanStack Query نیز شناخته میشود، از زمانی که به چارچوبهای مختلف وب منتقل شده است، به حق به یکی از محبوبترین کتابخانههای React تبدیل شده است. من در هر پروژه ای که درگیر آن هستم، چه به طور مستقیم یا از طریق tRPC، به آن دست پیدا می کنم. یک افسانه رایج این است که React Query یک کتابخانه واکشی داده است، که اینطور نیست. این ابزاری است که حالت ناهمزمان را همزمان می کند. بنابراین همچنان به ابزار واکشی داده نیاز دارید. ساخته شده در fetch
مرورگر API انتخاب پیش فرض است. کاری که React Query انجام می دهد این است که یک تابع واکشی ناهمزمان را بسته بندی می کند و آن را به عنوان داده های همزمان از طریق یک قلاب React به شما باز می گرداند. بیایید به یک مثال نگاه کنیم:
روش سنتی برای واکشی داده در کامپوننت React به شرح زیر است:
- خالی را آغاز کنید
useState
برای داده ها - ماشه واکشی داده در نصب
useEffect
- ذخیره داده های نتیجه با
setState
- داده های ذخیره شده در حالت باعث می شود که با داده ها دوباره ارائه شوند
به صورت کد:
function DataFetchingComponent() {
const [data, setData] = useState(null);
useEffect(() => {
const fetchData = async () => {
const response = await fetch('https://api.example.com/data');
const result = await response.json();
setData(result);
};
fetchData();
}, []);
if (!data) {
return div>Loading...div>;
}
return (
div>
pre>{JSON.stringify(data)}pre>
div>
);
}
با React Query می توانیم این را ساده کنیم:
function DataFetchingComponent() {
const fetchData = async () => {
const response = await fetch('https://api.example.com/data');
return response.json();
};
const { data, isLoading } = useQuery('data', fetchData);
if (!isLoading) {
return div>Loading...div>;
}
return (
div>
pre>{JSON.stringify(data)}pre>
div>
);
}
بسیار ظریف تر، اینطور نیست؟ اما این همه ماجرا نیست. حالا کد سفارشی را تصور کنید که باید بنویسید تا موارد زیر را مدیریت کنید:
- دسترسی به داده های یکسان در چندین مؤلفه بدون واکشی تکراری
- تازه کردن داده ها در طول جهش
- ذخیره و باطل کردن حافظه پنهان
- به روز رسانی های خوش بینانه
- واکشی مجدد پس زمینه
React Query همه اینها را برای شما مدیریت می کند! بنابراین جای تعجب نیست که این فناوری به یک فناوری اصلی در پروژه های من تبدیل شده است. چیزی که متوجه خواهید شد این است که هر دو نمونه هیچ نوع TypeScript مرتبطی ندارند. پس بیایید آن را درست کنیم.
ایمنی نوع API دستی
نمونه کدهای قبلی راهی برای شناخت انواع ندارند. ما دادهها را از یک نقطه پایانی HTTP معمولی که هیچ نوع اطلاعاتی را ارائه نمیکند، چه با یا بدون React Query، واکشی میکنیم. یک راه متداول برای ایمن کردن تایپ با تنظیم نوع TypeScript است. برخی از پروژهها این کار را با تولید کد انجام میدهند، به این معنی که یک اسکریپت وجود دارد که انواع را از backend مشخص میکند و سپس یک فایل تعریف نوع در frontend شما ایجاد میکند. این یک استراتژی معتبر است، اما نیاز به یک راه اندازی و فرآیند ساخت پیچیده دارد. بنابراین بسیاری از پروژه ها انتخاب می کنند که انواع را به صورت دستی تنظیم کنند.
// example data, could be anything
interface FetchedData {
id: number;
name: string;
}
function DataFetchingComponent() {
const fetchData = async (): PromiseFetchedData> => {
const response = await fetch('https://api.example.com/data');
return response.json();
};
const { data, isLoading } = useQuery('data', fetchData);
if (isLoading) {
return div>Loading...div>;
}
return (
div>
pre>{JSON.stringify(data, null, 2)}pre>
div>
);
}
این به خوبی کار می کند. شما data
اکنون شیء دارای نوع تعریف شده است FetchedData
. اما یک مشکل بزرگ وجود دارد: شما نمی توانید ثبات را تضمین کنید بین پاسخ و انواع شما TypeScript به شما احساس امنیت کاذب می دهد که مشخصه خاصی همیشه وجود دارد. شما هیچ راهی ندارید که بدانید آیا API ناگهان دیگر این ویژگی را در پاسخ شامل نمی شود.
اعتبار سنجی زود
برای اطمینان از سازگاری، ما به راهی برای تأیید پاسخ API نیاز داریم. در اینجا، زود وارد بازی می شود. شما طرحی را تعریف می کنید که توضیح می دهد پاسخ API چگونه باید باشد:
import { z } from 'zod';
const FetchedDataSchema = z.object({
id: z.number(),
name: z.string(),
});
تا اینجا این دقیقاً شبیه تعریف نوع ما است، اما با نحوی کمی متفاوت. اما بهترین بخش این است که ما می توانیم به طور خودکار یک تعریف نوع را بر اساس این طرح ایجاد کنیم.
type FetchedData = z.infertypeof FetchedDataSchema>;
اکنون می توانیم از این برای تأیید پاسخ API استفاده کنیم:
const FetchedDataSchema = z.object({
id: z.number(),
name: z.string(),
});
type FetchedData = z.infertypeof FetchedDataSchema>;
function DataFetchingComponent() {
const fetchData = async (): PromiseFetchedData> => {
const response = await fetch('https://api.example.com/data');
const data = await response.json();
return FetchedDataSchema.parse(data);
};
const { data, isLoading } = useQuery('data', fetchData);
if (isLoading) {
return div>Loading...div>;
}
return (
div>
pre>{JSON.stringify(data)}pre>
div>
);
}
را parse(data)
اگر پاسخ با طرحواره مطابقت نداشته باشد، تابع یک خطا ایجاد می کند. سپس می توانید آن خطا را هر طور که دوست دارید مدیریت کنید. اکنون ما گردش کار کاملی در مورد نحوه فراخوانی API های REST به شیوه ایمن از React با استفاده از React Query و Zod داریم. در یک برنامه تولیدی، شما میتوانید طرحوارهها و تابع واکشی را به فایلهای جداگانه منتقل کنید تا بتوان از آنها دوباره در برنامهتان استفاده کرد. اما جدای از آن، ما راه اندازی کاملی داریم که می تواند به برنامه های بزرگ مقیاس شود.