بناء Node.js / TypeScript REST API ، الجزء 1: Express.js

نشرت: 2022-03-11

كيف أكتب REST API في Node.js؟

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

انك سوف تحتاج:

  • معرفة أساسية بلغة JavaScript و TypeScript
  • المعرفة الأساسية بـ Node.js
  • المعرفة الأساسية بهندسة REST (راجع هذا القسم من مقالتي السابقة لـ REST API إذا لزم الأمر)
  • تثبيت جاهز لـ Node.js (يفضل الإصدار 14+)

في الطرفية (أو موجه الأوامر) ، سننشئ مجلدًا للمشروع. من هذا المجلد ، قم بتشغيل npm init . سيؤدي ذلك إلى إنشاء بعض ملفات مشروع Node.js الأساسية التي نحتاجها.

بعد ذلك ، سنضيف إطار عمل Express.js وبعض المكتبات المفيدة:

 npm i express debug winston express-winston cors

هناك أسباب وجيهة لأن هذه المكتبات مفضلة لدى مطوري Node.js:

  • debug هو وحدة نمطية سنستخدمها لتجنب استدعاء console.log() أثناء تطوير تطبيقنا. بهذه الطريقة ، يمكننا بسهولة تصفية بيانات التصحيح أثناء استكشاف الأخطاء وإصلاحها. يمكن أيضًا إيقاف تشغيلها بالكامل أثناء الإنتاج بدلاً من الاضطرار إلى إزالتها يدويًا.
  • winston مسؤول عن تسجيل الطلبات إلى واجهة برمجة التطبيقات الخاصة بنا وإرجاع الردود (والأخطاء). يتكامل Express express-winston مباشرةً مع Express.js ، بحيث يتم بالفعل تنفيذ جميع أكواد تسجيل winston القياسية المتعلقة بواجهة برمجة التطبيقات.
  • cors هو جزء من برمجية Express.js الوسيطة التي تسمح لنا بتمكين مشاركة الموارد عبر الأصل. بدون ذلك ، ستكون واجهة برمجة التطبيقات الخاصة بنا قابلة للاستخدام فقط من الواجهات الأمامية التي يتم تقديمها من نفس النطاق الفرعي تمامًا مثل نهايتنا الخلفية.

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

 npm i --save-dev @types/cors @types/express @types/debug source-map-support tslint typescript

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

يجب أن تكون التبعيات النهائية في package.json على النحو التالي:

 "dependencies": { "debug": "^4.2.0", "express": "^4.17.1", "express-winston": "^4.0.5", "winston": "^3.3.3", "cors": "^2.8.5" }, "devDependencies": { "@types/cors": "^2.8.7", "@types/debug": "^4.1.5", "@types/express": "^4.17.2", "source-map-support": "^0.5.16", "tslint": "^6.0.0", "typescript": "^3.7.5" }

الآن بعد أن تم تثبيت جميع التبعيات المطلوبة لدينا ، فلنبدأ في بناء الكود الخاص بنا!

هيكل مشروع TypeScript REST API

في هذا البرنامج التعليمي ، سننشئ ثلاثة ملفات فقط:

  1. ./app.ts
  2. ./common/common.routes.config.ts
  3. ./users/users.routes.config.ts

تكمن الفكرة وراء مجلدي بنية المشروع ( common users ) في الحصول على وحدات فردية لها مسؤوليات خاصة بها. بهذا المعنى ، سنحصل في النهاية على بعض أو كل ما يلي لكل وحدة:

  • تكوين المسار لتحديد الطلبات التي يمكن لواجهة برمجة التطبيقات لدينا التعامل معها
  • خدمات لمهام مثل الاتصال بنماذج قاعدة البيانات الخاصة بنا ، أو إجراء الاستعلامات ، أو الاتصال بالخدمات الخارجية التي يتطلبها الطلب المحدد
  • البرمجيات الوسيطة لتشغيل عمليات التحقق من صحة الطلبات المحددة قبل أن يعالج المتحكم النهائي للمسار تفاصيله
  • نماذج لتحديد نماذج البيانات المطابقة لمخطط قاعدة بيانات معين ، لتسهيل تخزين البيانات واسترجاعها
  • وحدات تحكم لفصل تكوين المسار عن الكود الذي يقوم أخيرًا (بعد أي برنامج وسيط) بمعالجة طلب المسار ، ويستدعي وظائف الخدمة المذكورة أعلاه إذا لزم الأمر ، ويعطي استجابة للعميل

توفر بنية المجلد هذه تصميمًا أساسيًا لـ REST API ، ونقطة بداية مبكرة لبقية سلسلة البرامج التعليمية هذه ، وهي كافية لبدء التدريب.

ملف المسارات المشتركة في TypeScript

في المجلد common ، لنقم بإنشاء ملف common.routes.config.ts ليبدو كما يلي:

 import express from 'express'; export class CommonRoutesConfig { app: express.Application; name: string; constructor(app: express.Application, name: string) { this.app = app; this.name = name; } getName() { return this.name; } }

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

الآن ، يمكننا البدء في إنشاء ملف توجيه المستخدمين. في مجلد users ، دعنا ننشئ users.routes.config.ts في ترميزه كما يلي:

 import {CommonRoutesConfig} from '../common/common.routes.config'; import express from 'express'; export class UsersRoutes extends CommonRoutesConfig { constructor(app: express.Application) { super(app, 'UsersRoutes'); } }

هنا ، نقوم باستيراد فئة CommonRoutesConfig وتوسيعها لتشمل فئة جديدة تسمى UsersRoutes . باستخدام المُنشئ ، نرسل التطبيق (الكائن الرئيسي express.Application ) والاسم UsersRoutes إلى CommonRoutesConfig .

هذا المثال بسيط للغاية ، ولكن عند القياس لإنشاء عدة ملفات مسار ، سيساعدنا هذا في تجنب تكرار الكود.

لنفترض أننا نريد إضافة ميزات جديدة في هذا الملف ، مثل التسجيل. يمكننا إضافة الحقل الضروري إلى فئة CommonRoutesConfig ، ومن ثم ستتمكن جميع المسارات التي تمتد CommonRoutesConfig من الوصول إليه.

استخدام وظائف TypeScript Abstract للوظائف المماثلة عبر الفئات

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

لنقم بإنشاء دالة مجردة بسيطة جدًا UsersRoutes فئة UsersRoutes (وفئات التوجيه المستقبلية) من CommonRoutesConfig . لنفترض أننا نريد إجبار جميع المسارات على أن يكون لها وظيفة (حتى نتمكن من تسميتها من المُنشئ المشترك) باسم configureRoutes() . هذا هو المكان الذي سنعلن فيه عن نقاط النهاية لكل مورد فئة توجيه.

للقيام بذلك ، سنضيف ثلاثة أشياء سريعة إلى common.routes.config.ts :

  1. abstract الكلمات الرئيسية لخط class الخاص بنا ، لتمكين التجريد لهذه الفئة.
  2. إعلان وظيفة جديدة في نهاية فصلنا ، abstract configureRoutes(): express.Application; . هذا يفرض على أي فئة تقوم بتوسيع CommonRoutesConfig لتوفير تطبيق يطابق ذلك التوقيع - إذا لم يكن كذلك ، فإن مترجم TypeScript سوف يتسبب في حدوث خطأ.
  3. استدعاء إلى this.configureRoutes(); في نهاية المُنشئ ، حيث يمكننا الآن التأكد من وجود هذه الوظيفة.

النتائج:

 import express from 'express'; export abstract class CommonRoutesConfig { app: express.Application; name: string; constructor(app: express.Application, name: string) { this.app = app; this.name = name; this.configureRoutes(); } getName() { return this.name; } abstract configureRoutes(): express.Application; }

مع ذلك ، يجب أن تحتوي أي فئة ممتدة CommonRoutesConfig على دالة تسمى configRoutes configureRoutes() تقوم بإرجاع كائن express.Application . هذا يعني أن users.routes.config.ts بحاجة إلى التحديث:

 import {CommonRoutesConfig} from '../common/common.routes.config'; import express from 'express'; export class UsersRoutes extends CommonRoutesConfig { constructor(app: express.Application) { super(app, 'UsersRoutes'); } configureRoutes() { // (we'll add the actual route configuration here next) return this.app; } }

تلخيصًا لما قمنا به:

نقوم أولاً باستيراد ملف common.routes.config ، ثم الوحدة النمطية express . نحدد بعد ذلك فئة UserRoutes ، ونقول إننا نريدها أن توسع الفئة الأساسية CommonRoutesConfig ، مما يعني أننا نعد بتنفيذ configRoutes configureRoutes() .

لإرسال معلومات إلى فئة CommonRoutesConfig ، نستخدم constructor الفصل. يتوقع أن يتلقى الكائن express.Application ، والذي سنصفه بمزيد من العمق في الخطوة التالية. باستخدام super() ، نمرر إلى CommonRoutesConfig للتطبيق واسم مساراتنا ، والتي في هذا السيناريو هي UsersRoutes. ( super() ، بدوره ، سوف يستدعي تطبيق configureRoutes() .)

تكوين مسارات Express.js لنقاط نهاية المستخدمين

إن وظيفة configureRoutes() هي المكان الذي سننشئ فيه نقاط النهاية لمستخدمي REST API. هناك ، سنستخدم التطبيق ووظائف المسار الخاصة به من Express.js.

الفكرة في استخدام وظيفة app.route() هي تجنب تكرار الكود ، وهو أمر سهل لأننا ننشئ واجهة برمجة تطبيقات REST بموارد محددة جيدًا. المصدر الرئيسي لهذا البرنامج التعليمي هو المستخدمون . لدينا حالتان في هذا السيناريو:

  • عندما يريد طالب واجهة برمجة التطبيقات إنشاء مستخدم جديد أو سرد جميع المستخدمين الحاليين ، يجب أن يكون لنقطة النهاية في البداية users في نهاية المسار المطلوب. (لن ندخل في تصفية طلبات البحث أو ترقيم الصفحات أو غيرها من الاستعلامات المماثلة في هذه المقالة.)
  • عندما يريد المتصل القيام بشيء محدد لسجل مستخدم معين ، فإن مسار مورد الطلب سيتبع النمط users/:userId .

تتيح لنا طريقة .route() في Express.js التعامل مع أفعال HTTP ببعض التسلسل الأنيق. هذا لأن .get() ، .post ( .post() ، وما إلى ذلك ، تُرجع جميعها نفس مثيل IRoute الذي يقوم به أول .route() . سيكون التكوين النهائي على النحو التالي:

 configureRoutes() { this.app.route(`/users`) .get((req: express.Request, res: express.Response) => { res.status(200).send(`List of users`); }) .post((req: express.Request, res: express.Response) => { res.status(200).send(`Post to users`); }); this.app.route(`/users/:userId`) .all((req: express.Request, res: express.Response, next: express.NextFunction) => { // this middleware function runs before any request to /users/:userId // but it doesn't accomplish anything just yet--- // it simply passes control to the next applicable function below using next() next(); }) .get((req: express.Request, res: express.Response) => { res.status(200).send(`GET requested for id ${req.params.userId}`); }) .put((req: express.Request, res: express.Response) => { res.status(200).send(`PUT requested for id ${req.params.userId}`); }) .patch((req: express.Request, res: express.Response) => { res.status(200).send(`PATCH requested for id ${req.params.userId}`); }) .delete((req: express.Request, res: express.Response) => { res.status(200).send(`DELETE requested for id ${req.params.userId}`); }); return this.app; }

يسمح الكود أعلاه لأي عميل REST API بالاتصال بنقطة نهاية users من خلال طلب POST أو GET . وبالمثل ، فإنه يتيح للعميل الاتصال بنقطة النهاية /users/:userId مع طلب GET أو PUT أو PATCH أو DELETE .

لكن بالنسبة إلى /users/:userId ، أضفنا أيضًا برمجيات وسيطة عامة باستخدام وظيفة all() ، والتي سيتم تشغيلها قبل أي من وظائف get() أو put() أو patch() أو delete() . ستكون هذه الوظيفة مفيدة عندما (لاحقًا في السلسلة) نقوم بإنشاء مسارات من المفترض أن يتم الوصول إليها فقط من قبل المستخدمين المصادق عليهم.

ربما لاحظت أنه في وظيفة .all() الخاصة بنا - كما هو الحال مع أي جزء من البرامج الوسيطة - لدينا ثلاثة أنواع من الحقول: Request و Response و NextFunction .

  • الطلب هو الطريقة التي يمثل بها Express.js طلب HTTP ليتم التعامل معه. يقوم هذا النوع بترقية وتوسيع نوع طلب Node.js الأصلي.
  • الاستجابة هي بالمثل الطريقة التي يمثل بها Express.js استجابة HTTP ، مما يوسع مرة أخرى نوع استجابة Node.js الأصلي.
  • لا تقل أهمية ، NextFunction هي بمثابة وظيفة رد اتصال ، مما يسمح بالتحكم بالمرور عبر أي وظائف وسيطة أخرى. على طول الطريق ، ستشارك جميع البرامج الوسيطة نفس كائنات الطلب والاستجابة قبل أن ترسل وحدة التحكم في النهاية ردًا إلى الطالب.

ملف نقطة الدخول Node.js الخاص بنا ، app.ts

الآن بعد أن قمنا بتكوين بعض الهياكل الأساسية للمسار ، سنبدأ في تكوين نقطة دخول التطبيق. دعنا ننشئ ملف app.ts في جذر مجلد مشروعنا ونبدأه بهذا الكود:

 import express from 'express'; import * as http from 'http'; import * as winston from 'winston'; import * as expressWinston from 'express-winston'; import cors from 'cors'; import {CommonRoutesConfig} from './common/common.routes.config'; import {UsersRoutes} from './users/users.routes.config'; import debug from 'debug';

اثنتان فقط من هذه الواردات جديدة في هذه المرحلة من المقالة:

  • http هو وحدة Node.js أصلية. يلزم بدء تشغيل تطبيق Express.js الخاص بنا.
  • body-parser هو برنامج وسيط يأتي مع Express.js. يقوم بتحليل الطلب (في حالتنا ، مثل JSON) قبل انتقال التحكم إلى معالجات الطلبات الخاصة بنا.

الآن بعد أن قمنا باستيراد الملفات ، سنبدأ في التصريح عن المتغيرات التي نريد استخدامها:

 const app: express.Application = express(); const server: http.Server = http.createServer(app); const port = 3000; const routes: Array<CommonRoutesConfig> = []; const debugLog: debug.IDebugger = debug('app');

تُرجع الدالة express() كائن تطبيق Express.js الرئيسي الذي سنمرره في جميع أنحاء الكود الخاص بنا ، بدءًا من إضافته إلى كائن http.Server . (سنحتاج إلى بدء تشغيل http.Server بعد تكوين تطبيق express.Application الخاص بنا.)

سنستمع إلى المنفذ 3000 - والذي سيستنتج TypeScript تلقائيًا أنه Number - بدلاً من المنافذ القياسية 80 (HTTP) أو 443 (HTTPS) لأن هذه المنافذ تُستخدم عادةً للواجهة الأمامية للتطبيق.

لماذا Port 3000؟

لا توجد قاعدة تقضي بأن يكون المنفذ 3000 - إذا كان غير محدد ، فسيتم تخصيص منفذ عشوائي - ولكن يتم استخدام 3000 في أمثلة التوثيق لكل من Node.js و Express.js ، لذلك نواصل التقليد هنا.

هل يمكن لـ Node.js مشاركة المنافذ مع الواجهة الأمامية؟

لا يزال بإمكاننا العمل محليًا في منفذ مخصص ، حتى عندما نريد من نهايتنا الخلفية الاستجابة للطلبات على المنافذ القياسية. سيتطلب هذا وكيلًا عكسيًا لتلقي الطلبات على المنفذ 80 أو 443 مع مجال معين أو مجال فرعي. سيقوم بعد ذلك بإعادة توجيههم إلى منفذنا الداخلي 3000.

ستقوم مصفوفة routes بتتبع ملفات المسارات الخاصة بنا لأغراض التصحيح ، كما سنرى أدناه.

أخيرًا ، سينتهي الأمر debugLog كوظيفة مشابهة لـ console.log ، ولكن الأفضل: من الأسهل ضبطه لأنه يتم تحديد نطاقه تلقائيًا إلى أي شيء نريد أن نطلق عليه سياق الملف / الوحدة. (في هذه الحالة ، أطلقنا عليه اسم "app" عندما مررنا ذلك في سلسلة إلى مُنشئ debug() .)

الآن ، نحن جاهزون لتهيئة جميع وحدات البرامج الوسيطة Express.js ومسارات واجهة برمجة التطبيقات الخاصة بنا:

 // here we are adding middleware to parse all incoming requests as JSON app.use(express.json()); // here we are adding middleware to allow cross-origin requests app.use(cors()); // here we are preparing the expressWinston logging middleware configuration, // which will automatically log all HTTP requests handled by Express.js const loggerOptions: expressWinston.LoggerOptions = { transports: [new winston.transports.Console()], format: winston.format.combine( winston.format.json(), winston.format.prettyPrint(), winston.format.colorize({ all: true }) ), }; if (!process.env.DEBUG) { loggerOptions.meta = false; // when not debugging, log requests as one-liners } // initialize the logger with the above configuration app.use(expressWinston.logger(loggerOptions)); // here we are adding the UserRoutes to our array, // after sending the Express.js application object to have the routes added to our app! routes.push(new UsersRoutes(app)); // this is a simple route to make sure everything is working properly const runningMessage = `Server running at http://localhost:${port}`; app.get('/', (req: express.Request, res: express.Response) => { res.status(200).send(runningMessage) });

يتم expressWinston.logger Express.js ، ويقوم بتسجيل التفاصيل تلقائيًا - عبر نفس البنية التحتية مثل debug - لكل طلب مكتمل. ستعمل الخيارات التي مررناها عليها على تنسيق وتلوين الإخراج الطرفي المقابل بدقة ، مع المزيد من التسجيل المطول (الافتراضي) عندما نكون في وضع التصحيح.

لاحظ أنه يتعين علينا تحديد مساراتنا بعد إعداد expressWinston.logger .

أخيرًا والأهم:

 server.listen(port, () => { routes.forEach((route: CommonRoutesConfig) => { debugLog(`Routes configured for ${route.getName()}`); }); // our only exception to avoiding console.log(), because we // always want to know when the server is done starting up console.log(runningMessage); });

هذا في الواقع يبدأ خادمنا. بمجرد أن يبدأ ، Node.js سوف يقوم بتشغيل وظيفة رد الاتصال الخاصة بنا ، والتي في وضع التصحيح تقوم بالإبلاغ عن أسماء جميع المسارات التي قمنا بتكوينها — حتى الآن ، UsersRoutes فقط. بعد ذلك ، يُعلمنا رد الاتصال بأن نهايتنا الخلفية جاهزة لتلقي الطلبات ، حتى عند التشغيل في وضع الإنتاج.

تحديث package.json إلى Transpile TypeScript إلى JavaScript وتشغيل التطبيق

الآن بعد أن أصبح لدينا الهيكل العظمي جاهزًا للتشغيل ، نحتاج أولاً إلى بعض التكوين المعياري لتمكين الترجمة من TypeScript. دعنا نضيف الملف tsconfig.json في جذر المشروع:

 { "compilerOptions": { "target": "es2016", "module": "commonjs", "outDir": "./dist", "strict": true, "esModuleInterop": true, "inlineSourceMap": true } }

ثم نحتاج فقط إلى إضافة اللمسات الأخيرة إلى package.json في شكل البرامج النصية التالية:

 "scripts": { "start": "tsc && node --unhandled-rejections=strict ./dist/app.js", "debug": "export DEBUG=* && npm run start", "test": "echo \"Error: no test specified\" && exit 1" },

البرنامج النصي test هو عنصر نائب سنقوم باستبداله لاحقًا في السلسلة.

ينتمي tsc في البرنامج النصي start إلى TypeScript. إنها مسؤولة عن تحويل شفرة TypeScript الخاصة بنا إلى JavaScript ، والتي سيتم إخراجها في مجلد dist . بعد ذلك ، نقوم فقط بتشغيل الإصدار المبني مع node ./dist/app.js .

مررنا --unhandled-rejections=strict لـ Node.js (حتى مع Node.js v16 +) لأنه من الناحية العملية ، فإن التصحيح باستخدام نهج "التعطل وإظهار المكدس" المباشر هو أكثر مباشرة من التسجيل المربي باستخدام كائن expressWinston.errorLogger . غالبًا ما يكون هذا صحيحًا حتى في الإنتاج ، حيث من المرجح أن يؤدي السماح لـ Node.js بالاستمرار في العمل على الرغم من الرفض غير المعالج إلى ترك الخادم في حالة غير متوقعة ، مما يسمح بحدوث المزيد من الأخطاء (وأكثر تعقيدًا).

يستدعي البرنامج النصي debug برنامج start النصي ولكنه يعرّف أولاً متغير بيئة DEBUG . يؤدي هذا إلى تمكين جميع debugLog() الخاصة بنا (بالإضافة إلى العبارات المماثلة من Express.js نفسها ، والتي تستخدم نفس وحدة debug التي نستخدمها) لإخراج تفاصيل مفيدة إلى المحطة الطرفية - التفاصيل التي تكون (بشكل ملائم) مخفية عند التشغيل الخادم في وضع الإنتاج مع npm start قياسي.

حاول تشغيل npm run debug بنفسك ، وبعد ذلك قارن ذلك بـ npm start لترى كيف يتغير إخراج وحدة التحكم.

نصيحة: يمكنك تقييد إخراج التصحيح app.ts debugLog() الخاصة بملف app.ts باستخدام DEBUG=app بدلاً من DEBUG=* . تتميز وحدة debug بشكل عام بالمرونة الشديدة ، وهذه الميزة ليست استثناءً.

ربما يحتاج مستخدمو Windows إلى تغيير export إلى SET لأن export هو كيف يعمل على نظامي التشغيل Mac و Linux. إذا كان مشروعك يحتاج إلى دعم بيئات تطوير متعددة ، فإن الحزمة الشاملة توفر حلاً مباشرًا هنا.

اختبار Live Express.js Back End

مع npm run debug npm start ، ستكون REST API جاهزة لخدمة الطلبات على المنفذ 3000. في هذه المرحلة ، يمكننا استخدام cURL و Postman و Insomnia وما إلى ذلك لاختبار النهاية الخلفية.

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

 curl --request GET 'localhost:3000/users/12345'

يجب أن ترسل نهايتنا الخلفية إجابة GET requested for id 12345 .

بالنسبة POST :

 curl --request POST 'localhost:3000/users' \ --data-raw ''

هذا وكل أنواع الطلبات الأخرى التي بنينا الهياكل العظمية من أجلها ستبدو متشابهة تمامًا.

تستعد لتطوير Rapid Node.js REST API مع TypeScript

في هذه المقالة ، بدأنا في إنشاء واجهة برمجة تطبيقات REST من خلال تكوين المشروع من البداية والغوص في أساسيات إطار عمل Express.js. بعد ذلك ، اتخذنا خطوتنا الأولى نحو إتقان TypeScript من خلال إنشاء نمط باستخدام UsersRoutesConfig بتوسيع CommonRoutesConfig ، وهو نمط سنعيد استخدامه للمقالة التالية في هذه السلسلة. انتهينا من تكوين نقطة دخول app.ts لدينا لاستخدام مساراتنا الجديدة و package.json مع البرامج النصية لإنشاء تطبيقنا وتشغيله.

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

المشروع الكامل متاح على GitHub ، والكود الموجود في نهاية هذه المقالة موجود في toptal-article-01 .