الـ JSON بيقبل أي حاجة
الـ JSON مجرد نص. الـ JSON.parse بيتأكد إن الأقواس والفواصل مظبوطة وبس. مش بيسأل عن المعنى خالص:
- مفيش types متفق عليها:
"30"و30الاتنين JSON سليم، بس واحد string والتاني number. - مفيش حقول إجبارية:
{}JSON سليم برضه، حتى لو الكود مستنيnameوemail. - مفيش منع لحقول زيادة: حد يبعتلك
"isAdmin": trueجوه الـ body، والـ parser هيقبله. - مفيش حدود: اسم طوله ١٠ ميجا، أو
qty: -5، أو array فيها مليون عنصر.
const body = JSON.parse('{"price": "100", "qty": 2}');console.log(body.price * body.qty); // 200 ← اشتغلت بالصدفةconsole.log(body.price + body.qty); // "1002" ← مصيبةconsole.log(body.discount.value); // TypeError: Cannot read properties of undefinedطب والـ TypeScript؟
الـ TypeScript بيراجع الكود وقت الكتابة، وبعد الـ compile الـ types كلها بتتمسح. يعني:
interface Order { price: number; qty: number;}// الـ TS مصدّقك على كلمتك، ومش بيراجع حاجةconst order = JSON.parse(rawBody) as Order;order.price.toFixed(2); // لو price جت string ← TypeError وقت التشغيل- الـ
as Orderكذبة مهذبة: إنت بتقول للـ compiler “صدقني”، وهو بيصدقك. - الـ
JSON.parseبيرجّعany: فالـ type safety بتقف عند أول بيانات جاية من بره. - الـ types مش موجودة في الـ JavaScript النهائي: مفيش حاجة اسمها
Orderوقت التشغيل عشان تراجع عليها.
حدود الثقة (trust boundaries)
أي مكان البيانات بتدخل فيه سيستمك من مصدر إنت مش متحكم فيه 100% هو trust boundary. هنا لازم تراجع:
- API body ← Validator
- Webhook ← Validator
- Config file ← Validator
- LLM output ← Validator
- Validator ← الكود بتاعك: valid
- Validator ← 400 / reject: invalid
- الـ API requests: الـ body والـ query والـ headers. أي حد يقدر يبعت أي حاجة بـ
curl. - الـ webhooks: service تانية بتبعتلك events. ممكن تغيّر شكلها في version جديد من غير ما تاخد بالك.
- ملفات الـ config: يوزر بيكتب
"port": "3000"أو بيغلط في اسم key. - رسايل الـ queue: service قديمة لسه بتبعت الشكل القديم في Kafka أو RabbitMQ.
- ردود الـ LLM: الموديل ممكن يرجّع field ناقص أو type غلط حتى لو طلبت منه JSON.
- بيانات قديمة في الداتابيز: document في MongoDB اتكتب من سنتين بشكل مختلف.
- الـ localStorage والـ URL params: على الـ frontend، أي حاجة اليوزر يقدر يعدّلها.
Fail fast: ارفض عند الباب
لو البيانات الغلط عدّت من الباب، الغلط بيظهر بعيد عن سببه:
- ارفض بدري: الـ request يرجع
400ومعاه رسالة بتقول/qty must be integer. - الغلط قريب من سببه: اللي بعت الغلط هو اللي بيعرف على طول، مش إنت بعد أسبوع.
- الكود اللي جوه يبقى أبسط: مش محتاج
if (typeof x === "number")في كل function، لأن الباب ضمنلك الشكل.
الحلول اللي قدامك
if (typeof body.name !== "string") في كل حتة. بيشتغل لأول ٣ حقول، وبعدين بيبقى كود طويل ومتكرر وناقص، ومحدش بيكتب رسالة error كويسة.
زي Zod أو Joi أو Yup. ممتازة جوه TypeScript/JavaScript، بس الـ schema كود، فمش هتشاركها مع service مكتوبة بـ Python أو Go بسهولة.
الـ schema نفسها JSON. أي لغة تقراها، وفيه validators لكل اللغات تقريبًا. وبتستخدمها OpenAPI والـ editors والـ LLM APIs وMongoDB.
formats binary ليها schema إجبارية. ممتازة بين الـ services الداخلية، بس مش JSON، والـ browser والـ APIs العامة غالبًا عايزين JSON.
خلّصت الفصل لما
١) الـ API بتاعك مكتوب بـ TypeScript strict. محتاج validation للـ body؟
req.body جاي من بره ومش مضمون. الـ TypeScript مش بيشوف البيانات وقت التشغيل.