البرمجة

استخدام Typescript مع Express

استخدام الأنواع التي توفرها TypeScript في تطبيقات Express

يعتبر استخدام TypeScript في تطوير تطبيقات Express من الممارسات الحديثة التي تعزز من جودة التعليمات البرمجية، وتجعلها أكثر أمانًا وقابلية للصيانة، خاصة في المشاريع المعقدة والمتوسطة إلى الكبيرة. إذ يوفر TypeScript طبقة إضافية من الأمان من خلال الأنواع الصارمة (Static Types) التي تساعد في التقليل من الأخطاء التي قد تظهر في وقت التشغيل، كما تسهل من عملية التتبع والتصحيح أثناء التطوير. في هذا المقال سنناقش بالتفصيل كيفية الاستفادة من ميزات الأنواع في TypeScript عند بناء تطبيقات باستخدام إطار Express، مع توضيح أمثلة عملية ونماذج متقدمة.


مقدمة عن TypeScript و Express

Express هو إطار عمل ويب شهير مبني على Node.js يُستخدم لإنشاء تطبيقات وخدمات الويب بسهولة وبسرعة. بينما JavaScript هي لغة البرمجة الأساسية التي يُكتب بها Express، فإن استخدام TypeScript يعزز من عملية التطوير من خلال إضافة دعم للأنواع، والكائنات الموجهة، والميزات الحديثة للغة.

TypeScript هو امتداد لـ JavaScript يوفر نظام أنواع قوي ونظام تصحيح ثابت، مما يسمح للمبرمجين بتجنب الأخطاء التي تظهر أثناء التنفيذ (Runtime Errors) عن طريق كشفها في مرحلة البناء (Compile Time).


أهمية الأنواع في تطبيقات Express

  1. تقليل الأخطاء البرمجية: عند استخدام أنواع ثابتة، يتمكن المحرر من اكتشاف الأخطاء قبل تشغيل التطبيق، مثل استخدام خاصية غير موجودة أو تمرير متغير من نوع خاطئ.

  2. تحسين تجربة المطور: التكامل بين TypeScript وأدوات التطوير الحديثة مثل VSCode يوفر ميزات ذكية كالإكمال التلقائي، والتنقل بين الكود، والتنبيهات المبكرة، مما يزيد من الإنتاجية.

  3. توثيق ذاتي للكود: الأنواع تُعتبر وثيقة مباشرة لوظائف الكود، مما يسهل على المطورين الآخرين فهم ما يتوقعه التطبيق من البيانات.

  4. التوافق مع مكتبات أخرى: يوفر مجتمع TypeScript تعريفات لأنواع مكتبات كثيرة منها express، مما يسمح باستخدامها بطريقة آمنة وسلسة.


الأنواع الأساسية في Express عند استخدام TypeScript

1. أنواع Request و Response و NextFunction

في Express، الطلب (Request) والرد (Response) هما الكائنان الرئيسيان اللذان يتم التعامل معهما. عند استخدام TypeScript، يمكن استيراد أنواع Request و Response و NextFunction من مكتبة express لتعريف المتغيرات في دوال الـ Middleware أو المعالجات.

typescript
import { Request, Response, NextFunction } from 'express'; app.get('/user/:id', (req: Request, res: Response, next: NextFunction) => { const userId = req.params.id; res.send(`User ID is ${userId}`); });

2. تخصيص أنواع الطلب (Request)

في كثير من الحالات، يحتوي جسم الطلب (req.body) أو معلمات الطلب (req.params) أو استعلام الطلب (req.query) على بيانات معينة، وينبغي تحديدها بوضوح لزيادة الأمان والوضوح.

على سبيل المثال، إذا كان لدينا جسم طلب متوقع يحتوي على خاصية name و age:

typescript
interface UserRequestBody { name: string; age: number; } app.post('/users', (req: Request<{}, {}, UserRequestBody>, res: Response) => { const { name, age } = req.body; res.send(`Received user: ${name}, Age: ${age}`); });

في هذا المثال:

  • {} هو مكان تحديد أنواع معلمات المسار (params) والتي في هذه الحالة غير مستخدمة.

  • {} هو مكان تحديد نوع الرد (Response Body) إن أردنا ذلك، وفي هذا المثال تركناه فارغًا.

  • UserRequestBody هو نوع جسم الطلب req.body.

هذا التخصيص يجعل من الممكن التعرف على الأخطاء مثل إرسال بيانات غير صحيحة أو مفقودة.

3. تخصيص معلمات المسار Query و Params

لنفترض أن لدينا مسارًا مع معلمات مسار أو استعلام:

typescript
interface UserParams { id: string; } interface UserQuery { filter?: string; } app.get('/users/:id', (req: Request, res: Response) => { const userId = req.params.id; const filter = req.query.filter; res.send(`User: ${userId}, Filter: ${filter || 'none'}`); });

في المثال أعلاه:

  • UserParams يحدد معلمات المسار (req.params).

  • UserQuery يحدد معلمات الاستعلام (req.query).

  • req.body غير مستخدم لذلك ترك فارغًا.


تعريف الأنواع المخصصة للميدلوير Middleware

الميدلوير في Express هي دوال يمكنها معالجة الطلبات قبل الوصول إلى المعالجات النهائية. مع TypeScript يمكن تحديد أنواع الطلب والرد في Middleware أيضًا.

مثال على Middleware يتحقق من وجود حقل في جسم الطلب:

typescript
function validateUser(req: Request<{}, {}, UserRequestBody>, res: Response, next: NextFunction) { const { name, age } = req.body; if (!name || !age) { return res.status(400).send('Missing required fields'); } next(); } app.post('/users', validateUser, (req: Request<{}, {}, UserRequestBody>, res: Response) => { res.send('User is valid'); });

استخدام الأنواع مع مكتبة express.RequestHandler

تُقدم مكتبة Express نوعًا يُدعى RequestHandler لتعريف Middleware أو المعالجات بطريقة عامة:

typescript
import { RequestHandler } from 'express'; const logger: RequestHandler = (req, res, next) => { console.log(`${req.method} ${req.url}`); next(); }; app.use(logger);

يمكن تخصيص هذا النوع ليتناسب مع أنواع معينة للجسم أو المعلمات:

typescript
const validateUser: RequestHandler<{}, {}, UserRequestBody> = (req, res, next) => { if (!req.body.name) { res.status(400).send('Name is required'); } else { next(); } };

أنواع الخطأ (Error Types) في Express

عند استخدام Middleware خاصة بمعالجة الأخطاء، يمكن تعريف الأنواع بدقة، حيث يتم تمرير كائن الخطأ أولاً:

typescript
import { ErrorRequestHandler } from 'express'; const errorHandler: ErrorRequestHandler = (err, req, res, next) => { console.error(err.stack); res.status(500).send('Something broke!'); }; app.use(errorHandler);

يمكن أيضًا تخصيص نوع الخطأ لتعريف المزيد من التفاصيل، مثل إضافة خاصية statusCode:

typescript
interface CustomError extends Error { statusCode?: number; } const errorHandler: ErrorRequestHandler = (err: CustomError, req, res, next) => { const status = err.statusCode || 500; res.status(status).send(err.message); };

الأنواع المتقدمة: استخدام Generics لتحديد أنواع ردود HTTP

في بعض التطبيقات، قد ترغب في تحديد أنواع الرد بدقة لتحسين التوافق مع الواجهات الأمامية أو REST APIs. TypeScript يسمح بذلك باستخدام Generics.

مثال على دالة مع Response تحتوي على نوع معين:

typescript
interface ApiResponse { data: T; message: string; } interface User { id: string; name: string; age: number; } app.get('/user/:id', (req: Request<{id: string}>, res: Response>) => { const user: User = { id: req.params.id, name: 'John Doe', age: 30, }; res.json({ data: user, message: 'User fetched successfully', }); });

استخدام مكتبات TypeScript مع Express

1. @types/express

لتسهيل استخدام TypeScript مع Express، يعتمد المطورون عادةً على الحزمة @types/express التي توفر التعريفات المناسبة للأنواع، وهي ضرورية لتكامل سلس.

لتثبيتها:

bash
npm install --save-dev @types/express

هذه الحزمة توفر جميع أنواع express الرسمية مثل Request، Response، NextFunction، وغيرها.

2. تكامل مع express-validator

للتأكد من صحة البيانات، يمكن استخدام مكتبة express-validator مع TypeScript لتوفير أنواع تحقق محسنة.

مثال:

typescript
import { body, validationResult } from 'express-validator'; app.post( '/user', body('email').isEmail(), body('age').isInt({ min: 0 }), (req: Request, res: Response) => { const errors = validationResult(req); if (!errors.isEmpty()) { return res.status(400).json({ errors: errors.array() }); } res.send('Valid data'); } );

توضيح استخدام الأنواع في مشروع Express نموذجي

لنفترض مشروع بسيط يحتوي على عدة مكونات، نوضح في الجدول التالي مثال على أنواع مخصصة للطلبات والاستجابات:

المكون النوع المستخدم الوصف
مسار المستخدم Request<{ id: string }, {}, User> معلمة المسار هي id، والرد هو كائن User
إضافة مستخدم Request<{}, {}, NewUserRequest> جسم الطلب يحتوي بيانات مستخدم جديد
تحديث مستخدم Request<{ id: string }, {}, UpdateUserRequest> معلمة المسار لتحديد المستخدم وجسم الطلب بيانات التحديث
رد الخطأ ErrorRequestHandler Middleware لمعالجة الأخطاء

أفضل الممارسات عند استخدام TypeScript مع Express

  • تحديد أنواع جميع المعاملات: عند التعامل مع المعلمات params، body، وquery يجب تحديد أنواع واضحة لتجنب الخطأ.

  • استخدام RequestHandler و ErrorRequestHandler: يساعد في توحيد تعريف الميدلوير وجعل الكود أكثر وضوحًا.

  • تخصيص الواجهات Interfaces: لتعريف شكل البيانات المرسلة والمستلمة، وهو أمر ضروري لتكامل API.

  • تثبيت تعريفات الأنواع: لجميع المكتبات الخارجية لتجنب فقدان الدعم الأنواع.

  • تفعيل خيارات TypeScript المشددة (strict mode): للحصول على أكبر قدر من الحماية من الأخطاء.


استنتاج

إن دمج TypeScript مع Express يمنح المطورين بيئة عمل متقدمة وقوية تسمح ببناء تطبيقات ويب أكثر أمانًا ووضوحًا، مع تقليل احتمالية ظهور الأخطاء وقت التشغيل. استخدام الأنواع المخصصة يساعد في ضمان صحة البيانات المتبادلة بين العميل والخادم، كما يُسهّل من عملية صيانة وتطوير الكود بمرور الوقت. من خلال تبني هذه الطريقة في البرمجة، يمكن للمطورين الوصول إلى مستويات عالية من الجودة والاحترافية في تطبيقاتهم المبنية على Express.


المصادر