Zod بالعربي: امنع TypeScript من تصديق JSON غلط
مستوى القارئ: متوسط
هتطلع من المقال وأنت عارف تحط بوابة تحقق واضحة بين الـ API والواجهة، بدل ما TypeScript يديك إحساس أمان مش موجود وقت التشغيل.
المشكلة باختصار
TypeScript ممتاز في منع أخطاء كثيرة أثناء الكتابة. لكنه لا يفتش JSON القادم من الشبكة وقت التشغيل. لو الـ API رجّع price كنص بدل رقم، أو حذف email من response، الكود ممكن يعدي من الـ compiler ويقع في المتصفح.
السيناريو الواقعي: عندك dashboard يعرض 20 ألف طلب يوميًا. 0.4% فقط من الردود جاية بشكل ناقص بسبب deploy قديم في backend service. الرقم يبدو صغيرًا، لكنه يعني 80 شاشة مكسورة يوميًا. الطريقة الشائعة الغلط هي كتابة as User بعد response.json(). الطريقة دي بتفشل لأنها لا تتحقق من أي شيء، هي بس بتسكت TypeScript.
مثال بسيط قبل التعريف
ركز في المثال ده: أنت مستلم كرتونة من المخزن مكتوب عليها "موبايلات". TypeScript يقرأ الملصق ويقولك تمام. Zod يفتح الكرتونة ويتأكد إن جواها موبايلات فعلاً، مش شواحن أو علب ناقصة.
بالظبط نفس الفكرة مع JSON. تعريف type User هو ملصق. أما z.object() فهو تفتيش فعلي عند الباب. لو البيانات سليمة تدخل التطبيق. لو غلط، ترفضها برسالة واضحة وتقدر تسجل الخطأ قبل ما يوصل للمستخدم.
الحل: اعمل schema عند حدود النظام
أفضل مكان لاستخدام Zod هو boundary: بعد fetch مباشرة، قبل تمرير البيانات للـ components أو تخزينها في cache. الافتراض إن المشروع TypeScript frontend أو Node.js backend بيتعامل مع JSON خارجي من API، webhook، queue، أو ملف config.
import { z } from "zod";
const UserSchema = z.object({
id: z.string().uuid(),
email: z.string().email(),
name: z.string().min(2),
plan: z.enum(["free", "pro", "team"]),
createdAt: z.coerce.date(),
});
type User = z.infer<typeof UserSchema>;
export async function getUser(userId: string): Promise<User> {
const res = await fetch(`/api/users/${userId}`);
const json: unknown = await res.json();
const parsed = UserSchema.safeParse(json);
if (!parsed.success) {
console.error("Invalid user payload", parsed.error.flatten());
throw new Error("User data is invalid");
}
return parsed.data;
}
لاحظ إننا استخدمنا unknown بدل any. ده قرار مقصود. unknown يجبرك تتحقق قبل الاستخدام. any يفتح الباب لكل حاجة. بعد safeParse، TypeScript يعرف إن مطابق للـ schema، وده اللي بيخلي الكود بعدها أنضف.