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

اليوم الأول تقديم Node.js
جئت إلى تطوير الخلفية. تستخدم شركة تكنولوجيا المعلومات هذه منصة
Node.js ، والتي لم أكن أعرفها تمامًا. ركضت للأمام قليلاً ، متجاهلة أن أخبر القارئ أنني لم أقم بتطوير أي شيء في جافا سكريبت (باستثناء بضع نصوص برمز نسخ). بشكل عام ، فهمت خوارزمية العمل وهيكل تطبيقات الويب ، حيث أنني طورت CRUD في Java و Python و Clojure ، لكن هذا لم يكن كافيًا. لذلك ، في اليوم الأول كرست بالكامل لدراسة Node.js ، ساعد هذا
screencast حقا.
أثناء دراسة إطار عمل
Express على الويب ، ومدير حزمة
npm ، وكذلك الملفات مثل package.json و tsconfig.json ، كان رأسي يتفقد كمية المعلومات. درس آخر هو أن إتقان جميع المواد في نفس الوقت قريب من المهمة المستحيلة. بحلول نهاية اليوم ، ما زلت أتمكن من تكوين البيئة وتمكنت من تشغيل خادم الويب السريع! ولكن كان من السابق لأوانه أن نفرح لأنه ذهب إلى المنزل مع شعور كامل بسوء الفهم. الشعور بأنني كنت أغرق في العالم الواسع لـ JS لم يتركني لمدة دقيقة ، لذلك كانت هناك حاجة لإعادة تشغيل الكمبيوتر.
اليوم الثاني. تقديم TypeScript
نفس إعادة التشغيل تبعت ذلك اليوم بالذات. في هذه المرحلة ، أدركت مشكلتي تمامًا ، وسوف ننتقل إليها قليلًا. مع العلم أنه لم يكن من الضروري الكتابة بلغة JavaScipt الخالصة ، فإن التدريب من Node.js كان يتدفق بسلاسة إلى لغة TypeScript ، أي ميزاته وبناء الجملة. رأيت هنا
الأنواع التي طال انتظارها ، والتي بدونها كانت البرمجة حرفيًا قبل يومين ،
وليس بلغات البرمجة الوظيفية. كان هذا أكبر اعتقادي الخاطئ ، الذي منعني من فهم وتعلم الكود المكتوب بلغة JavaScript في اليوم الأول.
وقد سبق له أن كتب لغالبية لغات البرمجة الموجهة للكائنات مثل Java و C ++ و C #. تحقيق إمكانيات TypeScript ، شعرت بالراحة. هذه لغة البرمجة حرفت في نفسي حياة هذه البيئة المعقدة ، كما بدا لي في ذلك الوقت. قرب نهاية اليوم الذي قمت فيه بإعداد البيئة بالكامل ، أطلقت الخادم (الموجود بالفعل على TypeScript) ، وربط المكتبات الضرورية التي سأناقشها أدناه. خلاصة القول: جاهزة لتطوير API. نمر مباشرة إلى التنمية ...
تطوير API
شرحًا لمبدأ العمل وتفسيرات أخرى حول ماهية REST API ، سنترك ، لأن المنتدى يحتوي على الكثير من المقالات حول هذا الموضوع مع أمثلة وتطوير بلغات برمجة متنوعة.
كانت المهمة على النحو التالي:تقديم خدمة باستخدام واجهة برمجة تطبيقات REST. إذن بواسطة الرمز المميز لحامل (/ info ، / الكمون ، / تسجيل الخروج). CORS تكوين للوصول من أي مجال. DB - MongoDB. إنشاء رمز مميز في كل مكالمة.
وصف API:- / signin [POST] - حامل رمز الطلب بواسطة id وكلمة المرور // يستقبل البيانات في json
- / تسجيل [POST] - تسجيل مستخدم جديد: // يستقبل البيانات في json
- / info [GET] - تقوم بإرجاع معرف المستخدم ونوع المعرف ، ويتطلب الرمز المميز الصادر من حامل في المصادقة
- / زمن الوصول [GET] - يُرجع التأخير (ping) ، ويتطلب الرمز المميز الصادر عن حامله في المصادقة
- / logout [GET] - مع المعلمة all: صواب - حذف جميع الرموز المميزة لحامل المستخدم أو false - حذف رمز حامل البطاقة الحالي فقط
ألاحظ على الفور ، تبدو المهمة بسيطة بشكل لا يصدق لمطور تطبيق الويب. ولكن يجب تنفيذ المهمة بلغة برمجة ، والتي منذ 3 أيام لم تكن تعرف أي شيء على الإطلاق! حتى بالنسبة لي ، يبدو شفافًا تمامًا على الورق وفي بيثون استغرق التنفيذ بعض الوقت ، لكن لم يكن لدي مثل هذا الخيار. كومة التنمية تنقل المتاعب.
وسائل التنفيذ
لذلك ، ذكرت أنه في اليوم الثاني درست بالفعل عدة مكتبات (أطر) ، سنبدأ من هذا. بالنسبة للتوجيه ، اخترت أدوات
التحكم في التوجيه ، مسترشدة بالكثير من أوجه التشابه مع الديكورات من Spring Framework (Java). بصفتي ORM ، اخترت
typeorm ، على الرغم من أنني أعمل مع MongoDB في الوضع التجريبي ، إلا أنها كافية لمثل هذه المهمة. اعتدت
uuid لإنشاء الرموز ، يتم تحميل المتغيرات باستخدام
dotenv .
بدء تشغيل خادم الويب
عادةً ما يتم استخدام Express في شكله النقي ، لكنني ذكرت إطار عمل Routing Controllers ، والذي يسمح لنا بإنشاء خادم سريع على النحو التالي:
كما ترون ، لا يوجد شيء معقد. في الواقع ، يحتوي الإطار على ميزات أكثر من ذلك بكثير ، ولكن لم تكن هناك حاجة إليها.
- يعد routePrefix مجرد بادئة في عنوان url الخاص بك بعد عنوان الخادم ، على سبيل المثال: localhost : 3000 / prefix
- الافتراضيات - لا شيء مثير للاهتمام ، مجرد تهيئة رموز الخطأ
- AuthorizationChecker - فرصة عظيمة للإطار للتحقق من ترخيص المستخدم ، ثم سننظر في مزيد من التفاصيل
- وحدات التحكم هي واحدة من الحقول الرئيسية حيث نختار وحدات التحكم المستخدمة في تطبيقنا
اتصال DB
في وقت سابق ، أطلقنا بالفعل خادم الويب ، لذلك سوف نستمر في الاتصال بقاعدة بيانات MongoDB ، بعد أن نشرناه في السابق على الخادم المحلي. يتم وصف التركيب والتكوين بالتفصيل في
الوثائق الرسمية . سننظر مباشرة في الاتصال باستخدام typeorm:
كل شيء بسيط للغاية ، تحتاج إلى تحديد العديد من المعلمات:
- نوع - DB
- عنوان IP للمضيف حيث قمت بنشر قاعدة البيانات
- قاعدة البيانات - اسم قاعدة البيانات التي تم إنشاؤها مسبقا في mongodb
- تزامن - التزامن التلقائي مع قاعدة البيانات (ملاحظة: كان من الصعب السيطرة على الترحيل في ذلك الوقت)
- الكيانات - هنا نشير إلى الكيانات التي يتم إجراء التزامن بها
نحن الآن نربط إطلاق الخادم والاتصال بقاعدة البيانات. ألاحظ أن استيراد الموارد يختلف عن المورد الكلاسيكي المستخدم في Node.js. نتيجة لذلك ، نحصل على الملف القابل للتنفيذ التالي ، في حالتي main.ts:
import 'reflect-metadata'; import * as dotenv from 'dotenv'; import { createExpressServer } from 'routing-controllers'; import { createConnection } from 'typeorm'; import { authorizationChecker } from './auth/authorizationChecker'; import { UserController } from './controllers/UserController'; import { User } from './models/User'; dotenv.config();
حقيقة
اسمحوا لي أن أذكركم بأن المهمة تتمثل في مصادقة المستخدمين وترخيصهم ، على التوالي ، نحتاج إلى كيان: مستخدم. ولكن هذا ليس كل شيء ، لأن كل مستخدم لديه رمز وليس واحدًا! لذلك ، من الضروري إنشاء كيان رمز.
المستخدم import { ObjectID } from 'bson'; import { IsEmail, MinLength } from 'class-validator'; import { Column, Entity, ObjectIdColumn } from 'typeorm'; import { Token } from './Token';
في جدول المستخدم ، نقوم بإنشاء حقل - مجموعة من الرموز المميزة للمستخدم. نقوم أيضًا
بتمكين calss-validator ، لأنه من الضروري أن يسجل المستخدم الدخول عبر البريد الإلكتروني.
رمز import { Column, Entity } from 'typeorm';
القاعدة هي كما يلي:

إذن المستخدم
للحصول على إذن ، نستخدم
AuthorizationChecker (إحدى المعلمات عند إنشاء الخادم ، انظر أعلاه) ، للراحة ، نضعه في ملف منفصل:
import { Action, UnauthorizedError } from 'routing-controllers'; import { getMongoRepository } from 'typeorm'; import { User } from '../models/User'; export async function authorizationChecker(action: Action): Promise<boolean> { let token: string; if (action.request.headers.authorization) {
بعد المصادقة ، يكون لكل مستخدم
رمزه المميز ، حتى نتمكن من الحصول على الرمز المميز من رؤوس الاستجابة ، يبدو كما يلي:
Bearer 046a5f60-c55e-11e9-af71-c75526de439e . الآن يمكننا التحقق من وجود هذا الرمز المميز ، وبعد ذلك تقوم الدالة بإرجاع معلومات الترخيص: صواب - المستخدم مخول ، خطأ - المستخدم غير مخول. في التطبيق ، يمكننا استخدام ديكور مناسب للغاية في وحدة التحكم:Authorized (). في هذه المرحلة ، سيتم استدعاء وظيفة AuthorizationChecker ، والتي ستُرجع استجابة.
منطق
بادئ ذي بدء ، أود أن أصف منطق العمل ، نظرًا لأن وحدة التحكم عبارة عن سطر واحد من مكالمات الطريقة أسفل الفئة المقدمة. أيضًا ، في وحدة التحكم ، سوف نقبل جميع البيانات ، وفي حالتنا ، ستكون JSON و Query. سننظر في أساليب المهام الفردية ، وفي النهاية سنقوم بتكوين الملف النهائي ، والذي يسمى UserService.ts. ألاحظ أنه في ذلك الوقت لم تكن هناك معرفة كافية للقضاء على التبعيات. إذا لم تستوفِ المصطلح "حقن التبعية" ، فإنني أوصي بشدة بالقراءة عنه. في الوقت الحالي ، أستخدم إطار عمل DI ، أي استخدام الحاويات ، أي الحقن من خلال الصانعين. هنا ، كما أعتقد ،
مقال جيد للمراجعة. نعود إلى المهمة.
- / تسجيل الدخول [POST] - مصادقة المستخدم المسجل. كل شيء بسيط جدا وشفاف. نحتاج فقط إلى العثور على هذا المستخدم في قاعدة البيانات وإصدار رمز مميز جديد. للقراءة والكتابة ، يتم استخدام MongoRepository.
async userSignin(user: User): Promise<string> {
- / تسجيل [POST] - تسجيل مستخدم جديد. طريقة مشابهة جدًا ، حيث أننا في البداية نبحث أيضًا عن مستخدم حتى لا يكون لدينا مستخدمون مسجلون لديهم بريد إلكتروني واحد. بعد ذلك ، نكتب المستخدم الجديد إلى قاعدة البيانات ، بعد إصدار الرمز المميز.
async userSignup(newUser: User): Promise<string> {
- / info [GET] - تقوم بإرجاع معرف المستخدم ونوع المعرف ، ويتطلب الرمز المميز الصادر من حامل في المصادقة. تكون الصورة شفافة أيضًا: أولاً نحصل على رمز المستخدم الحالي من رؤوس الطلبات ، ثم نبحث عنه في قاعدة البيانات ونحدد من يكمن فيه ، ونعيد العثور على المستخدم.
async getUserInfo(req: express.Request): Promise<User> {
- / زمن الوصول [GET] - يُرجع التأخير (ping) ، ويتطلب الرمز المميز الصادر عن حامله في المصادقة. فقرة رتيبة تماما من المادة ، وإن كان. لقد استعملت هنا مكتبة جاهزة فقط للتحقق من تأخير tcp-ping.
getLatency(): Promise<IPingResult> { function update(progress: number, total: number): void { console.log(progress, '/', total); } const latency = ping({ address: process.env.PING_ADRESS, attempts: Number(process.env.PING_ATTEMPTS), port: Number(process.env.PING_PORT), timeout: Number(process.env.PING_TIMEOUT) }, update).then(result => { console.log('ping result:', result); return result; }); return latency; }
- / logout [GET] - مع المعلمة all: true - يحذف كافة الرموز المميزة لحامل المستخدم أو false - يحذف فقط الرمز المميز للحامل الحالي. نحتاج فقط إلى العثور على المستخدم ، والتحقق من معلمة الاستعلام وإزالة الرموز. أعتقد أن كل شيء يجب أن يكون واضحا.
async userLogout(all: boolean, req: express.Request): Promise<void> {
مراقب
لا يحتاج الكثيرون إلى شرح ما هو مطلوب وكيف يتم استخدام وحدة التحكم في نمط MVC ، لكنني ما زلت أقول كلمتين. باختصار ، وحدة التحكم هي الرابط بين المستخدم والتطبيق الذي يعيد توجيه البيانات بينهما. أعلاه ، تم وصف المنطق تمامًا ، حيث يتم استدعاء الأساليب وفقًا للطرق التي تتكون من URI وخادم IP
(على سبيل المثال: localhost: 3000 / signin) . ذكرت في وقت سابق عن الديكور في وحدة التحكم:
Get ،
POST ،Authorized والأهم منهم هو @ JsonController. ميزة أخرى مهمة للغاية في هذا الإطار هي أننا إذا أردنا إرسال واستقبال JSON ، فإننا نستخدم هذا الديكور بدلاً من
وحدة التحكم .
import * as express from 'express'; import { Authorized, Body, Get, Header, JsonController, NotFoundError, Post, QueryParam, Req, UnauthorizedError } from 'routing-controllers'; import { IPingResult } from '@network-utils/tcp-ping'; import { User } from '../models/User'; import { UserService } from '../services/UserService';
استنتاج
في هذه المقالة ، أردت أن أعكس المكون الفني للرمز الصحيح أو شيء من هذا القبيل ، ولكن لمشاركة حقيقة أنه يمكن لأي شخص إنشاء تطبيق ويب باستخدام قاعدة بيانات ويحتوي على بعض المنطق على الأقل من الصفر المطلق في
خمسة أيام. مجرد التفكير في ذلك ، لم يكن هناك أداة مألوفة ، تذكر نفسك أو مجرد وضعها في مكاني. ليست هذه هي الحالة التي تقول: "أنا الأفضل ، لا يمكنك فعل ذلك أبدًا". على العكس ، هذه صرخة من روح شخص يسعد تمامًا حاليًا بعالم Node.js ويشاركه معك. وحقيقة أنه لا يوجد شيء مستحيل ، ما عليك سوى أن تفعل وتفعل!
بالطبع ، لا يمكن إنكار أن المؤلف لم يعرف شيئًا وجلس لكتابة الكود لأول مرة. لا ، كانت معرفة OOP ومبادئ REST API و ORM وقاعدة البيانات موجودة بكميات كافية. ويمكن أن نقول هذا فقط أن وسائل تحقيق النتيجة لا تؤدي مطلقًا أي دور وقول في الأسلوب: "أنا لن أذهب إلى هذه الوظيفة ، هناك لغة برمجة لم أتعلمها" ، بالنسبة لي الآن مجرد مظهر من مظاهر الشخص وليس الضعف ، بل الحماية من بيئة خارجية غير مألوفة. ولكن ما الذي يوجد للاختباء كان الخوف موجودًا معي.
لتلخيص. أريد أن أنصح الطلاب والأشخاص الذين لم يبدأوا حياتهم المهنية في مجال تكنولوجيا المعلومات بعد ، حتى لا يخافوا من أدوات التطوير والتقنيات غير المعروفة. بالتأكيد سوف يساعدك الرفاق الكبار (إذا كنت محظوظًا كذلك) ، فسيقومون بالتفصيل والإجابة على الأسئلة ، لأن كل واحد منهم كان في هذا المنصب. ولكن لا تنس أن رغبتك هي الجانب الأكثر أهمية!
رابط
للمشروع