ساخت برنامه فول استک وب با Nest js و Angular با استفاده از NX

چرا این پست؟بعد از اینکه تصمیم به انتخاب یک فریم ورک برای سمت فرانت گرفتم Angular روانتخاب خوبی دیدم و چون سمت سرور هم فریم ورکی به نام Nest js داریم که خیلی شبیه ساختار Angular هست و هر دو هم با تایپ اسکریپت کار میکنن به ابزاری رسیدم به نام NX که هر دو فریم ورک سمت بکند و فرانت رو باهم جفت میکنه و معجون دلنشینی درست میکنه که مدیریتشون خیلی آسون میشه و واقعا خوب کار میکنن و خب توصیه میکنم کسانی که قصد ساخت برنامه کامل دارن از این جفت فریم ورک استفاده کنن که NX هم به خوبی اون‌ها رو جفت میکنه واسمون و خب شاید این پست باعث شد یکبار هم که شده از Nx و ترکیب این دو فریم ورک جذاب استفاده کنین.خب Angular فریم ورک پیشفرض ما برای سمت فرانتمون هست و ما از Nest هم برای سمت بکند خودمون استفاده میکنیم و هر دوی اینها با NX در یک فضای کاری (workspace) کنار هم قرار میگرن (بهتره بگیم باهم ازدواج میکنن :دی) و نهایتا هم هر دو پروژه فرانت و بکند رو در قالب یک پکیج واحد برای انتشار در سرور بسته بندی میکنیمبرای جمع بندی سریع NestJs یک لایه انتزاعی هست که روی دو فریم ورک ExpressJs و Fastify قرار گرفته که هر دو از فریم ورکهای خیلی محبوب NodeJS هست و انتخاب اینکه با کدوم یکی فریم ورک کار کنه با خودمون هست. نست با TypeScript نوشته شده و دقیقا همون الگو و ساختار Angular رو داره و NX هم مجموعه از ابزارهای توسعه یافته توسط ex-Googlers هست.آماده سازیاول یک فضای کار NX جدید رو پیکربندی میکنیم و یک فریم ورک انگولار و نست داخل اون ایجاد میکنیم (هرجا که میخواین پروژه خودتون رو بسازین ترمینال رو اجرا کنین و دستور زیر رو وارد کنین):npx create-nx-workspace@latestبعد از ایجاد فضای کاری جدید NX شروع به پرسیدن چند سوال برای پیکربندی مورد نظر شما می‌کنه که مثل تصویر زیر عمل کنین:سوالات پیکربندیبعد از انجام مراحل بالا صبر کنین تا پیکربندی و نصب برنامه کامل بشه.ایجاد یک فضای نام به نام test.ساخت یک برنامه سمت کاربر با انگولار به نام client که در مسیر test/apps/client قابل مشاهده هست.ساخت یک سرویس با نام api که در مسیر test/apps/api قابل مشاهده هست.اما در ریشه test یک دایرکتوری به نام libs هم داریم که واسه ساخت interface های مشترک بین انگولار و نست هست که در هردو قابل استفاده باشن.اما چیزی که همچنان جالب هست ابزارهای بهبود یافته برای هر دو پروژه بکند و فرانت هست:استفاده از Jest برای تست واحد.استفاده از cypress برای e2e specs (فقط انگولار).استفاده از Prettier برای فرمت و قالب بندی خودکار کدها.تنظیمات مناسب TSLint برای کل فضای کار.اجرای پروژه ها:حالا که پروژه ها رو تنظیم کردیم ، زمان راه اندازی اونها شده. دو پنجره ترمینال رو در ریشه فضای کاری باز کنین و دستورات زیر رو وارد کنین:[Terminal 1]: npm run nx -- serve client[Terminal 2]: npm run nx -- serve apiاولین دستور سرور پیش فرض Angular رو اجرا می‌کنه ، frontend را تحت http://localhost:4200 کامپایل و ارائه میکنه.مورد دوم هم سرور توسعه دهنده NestJS رو راه اندازی می کنه ، Typescript رو به JavaScript سازگار با NodeJS کامپایل می کنه و نتیجه را هم در http://localhost: 3333/api ارائه می‌ده.به عنوان یک پاداش خوب ، Nx پیکربندی پروکسی رو برای Angular dev-server اضافه کرده ، که درخواست پروکسی رو برای http://localhost: 4200/api به سرور NestJS dev-server برای ما ارسال می‌کنه ، به طوری که با هیچ مشکلی از جمله خطاهای آزار دهنده CORS روبرو نشیم. پیکربندی پروکسی رو می تونیم در مسیر apps/client/proxy.conf.json پیدا کنیم. حالا مرورگر خودمون رو با آدرس http://localhost:4200 باز کنیم ، باید صفحه‌ای مشابه این صفحه رو ببینیم:در صورت بازبینی شبکه و رفرش صفحه، یک درخواست XHR برای http://localhost:4200/api/hello مشاهده میکنیم که خبر از تظیم درست پروکسی به ما میده.وایستین چی شد؟!اجازه بدین در مورد چیزی که رخ میده توضیح بدم:پروژه NestJS با یک prefix پیکربندی شده تا برای همه کنترلرها قابل استفاده باشه. و این prefix از این مسیر (apps/api/src/main.ts) قابل دسترسی هست و میتونین به دلخواه عوض کنین:const globalPrefix = 'api'; app.setGlobalPrefix(globalPrefix);و همینطور NX برامون یک نمونه کنترلر REST با نام AppController ایجاد کرده که اینجا میتونین پیداش کنین: apps/api/src/app/app.controller.tsکه در حال حاضر یک داده استاتیک رو ارائه می‌ده ، و یک شی اینترفیس Typescript به نام Message هم داریم که مانند این تعریف شده (libs/api-interfaces/src/lib/api-interfaces.ts):export interface Message { message: string; }چون این اینترفیس در مسیر libs ساخته شده و این یک shared library هست پس بصورت اشتراکی میتونه هم سمت فرانت و هم سمت بکند استفاده بشه. جذابه یک نوع type-safety در هر دو پروژه!درون کنترلر AppController هم که محتویات زیر رو داریم:@Get('hello') getData( ): Message { return this.appService.getData( ); }که به NestJS می‌گه یک GET-endpoint جدید رو به http://localhost:3333/api/hello درخواست بده و زمانی که سرور در حال اجراست ، آبجکت Message را به ما برمیگردونه.اون سمت دیگه ما پشته پروژه Angular رو داریم که برای انجام یک نمونه درخواست GET به آدرس بالا پیکربندی شده. و کدی که این کار رو برامون انجام میده رو میتونیم به راحتی تو آدرس اینجا پیدا کنیم: apps/client/src/app/app.component.tsexport class AppComponent { hello$ = this.http.get<Message>('/api/hello'); constructor(private http: HttpClient) {} }و خب نهایتا هم در تمپلیت فراخوانی میکنیم:<div>Message: {{ hello$ | async | json }}</div>اکنون ، از آنجا که Angular درhttp://localhost:4200 در حال اجراست ، به دلیل مسیر نسبی (relative) در فراخوانی httpClient ، درخواست به http://localhost/4200/api/hello ارسال می‌شه. این مورد توسط پیکربندی پراکسی Angular تنظیم شده و به سرور NestJS ما ارسال می‌شه. عالیه!دستکاری نرم افزار برای توسعهتبریک می‌گم ، حالا ما یک فضای کاری داریم که هم NestJS و هم Angular در کنار همدیگه اجرا میشن و همچنین کد نمونه ای در مورد چگونگی صدا زدن بکند از سمت فرانت وجود دارد. که در حال حاضر خیلی زیباس!خب اگه یادتون باشه بالاتر اومدیم یکی یکی هر دو پروژه بکند و فرانت رو با دستورات مربوط به خودش اجرا کردیم حالا کاری که من می‌خوام انجام بدم تا یک تجربه توسعه بهتر داشته باشیم ، شروع همزمان سرورهای فرانت و بکند به طور موازی با یک دستور هست. برای این کار ما یک ماژول کمکی به نام concurrently نصب میکنیم.npm install --save-dev concurrentlyحالا قطعه کد زیر رو کافیه تو script فایل package.json قرار بدیم:&quotstart:fe&quot: &quotng serve client&quot, &quotstart:be&quot: &quotng serve api&quot, &quotdev&quot: &quotconcurrently -p=\&quot{name}\&quot -n=\&quotAngular,NestJS\&quot -c=\&quotgreen,blue\&quot \&quotnpm run start:fe\&quot \&quotnpm run start:be\&quot&quotحالا با اجرای npm run dev هر دو پروژه به طور موازی اجرا می‌شن ، هر خط از لاگ کنسول رو با پیشوند "Angular" یا "NestJS" با رنگ سبز و آبی رنگ میبینیم. عالی!هر دو سرور در حال اجرا شدنبرای محیط production از frontend و backend بصورت یک سرور واحد استفاده کنینخب ، حالا ما یک برنامه کاملاً فول استک آماده برای توسعه داریم. بیایید با بسته بندی برنامه خودمون به عنوان یک بسته npm اجرایی واحد که سمت فرانت و بکند رو سرو کنه.برای دستیابی به این هدف، ما یک build محیط production از برنامه Angular خود ایجاد میکنیم و سرور NestJS خودمون رو هم پیکربندی می‌کنیم تا در مسیر اصلی root بتونه frontend رو سرویس دهی کنه ، به طوری که بتونیم هر دو رو در یک بسته بصورت خودکار بسته بندی کنیم (برای استقرار آن به عنوان مثال در یک سرور شخصی مجازی یا هر سرویس ابری).اول ما یک build پروداکشن از پروژه Angular ایجاد میکنیم:npm run nx -- build client --prodدر حال ایجاد یک نسخته prodحالا صفحات پیشین کامپایل شده (minified ، uglified ، polyfiled) رو می تونین در مسیر زیر مشاهده کنین:dist/apps/clientبیایید NestJS رو طوری پیکربندی کنیم که وقتی بکند سرو میشه به پوشه اصلی اون دسترسی پیدا کنیم.اول ما ماژول serve-static رو در NestJs نصب می کنیم تا امکان دسترسی به فایل‌های استاتیک رو برای ما فراهم کنه.حالا تنها کاری که ما باید انجام بدیم این هست که ServeStaticModule ارائه شده توسط این بسته رو در AppModule ایمپورت و پیکربندی کنیم (apps/api/src/app/app.module.ts).import { ServeStaticModule } from '@nestjs/serve-static'; import { join } from 'path'; //... @Module({ imports: [ ServeStaticModule.forRoot({ rootPath: join(__dirname, '..', 'client') }) ], //... }) export class AppModule {} به NestJS می‌گه که درون client/.. رو از فولدر dist خودت از مسیر dist/apps/api پیمایش کن و محتویات اون رو در rootPath سرو کن.و مطمئناً کافی هست که فقط backend رو از طریق npm run start:be اجرا کنین: و کافیه آدرس http://localhost:3333/ رو باز کنین و باید همون نتیجه قبلی رو داشته باشین.مثل بار اول باید اجرا بشهپکیج کردن اپلیکیشن برای دیپلوی کردن روی سرورتنها چیزی که باقی مونده این هست که همه چیز رو در یک ماژول npm قرار بدیم و ما آماده استقرار برنامه فول استک خودمون خواهیم بود. بیایین حالا این کار را انجام بدیم.ما باید package.json را ویرایش کنیم تا فقط شامل پرونده های dist باشه و به برای راحتی یک اسکریپت serve به اون اضافه می کنیم:{ ... &quotfiles&quot: [ &quotdist/apps/client&quot, &quotdist/apps/api&quot ], ... &quotscripts&quot: { &quotserve&quot: &quotnode dist/apps/api/main.js&quot, } ... }حالا ، اگه npm pack رو اجرا کنیم ، یک فایل tarball برای ما در فهرست پروژه ایجاد می‌شه:ساخت فایل tarballبعد از اجرای دستور npm pack یک فایل با نام و پسوند test-0.0.0.tgz ساخته میشه که قابل انتقال به سرور هست که test همون نام workspace شماست. بعد از بارگزاری روی سرور باید برنامه خودمون رو که در قالب یک فایل هست با دستور npm install --production نصب کنیم. البته شما می تونین این بسته رو به صورت محلی هم اجرا کنین، به سادگی از بسته بندی فایل tarball ، با اجرای npm install --productionدر داخل دایرکتوری مورد نظر و سپس اجرای npm run serveاستفاده کنین و لذتش رو ببرین.پست طولانی بود خوشحال میشم جایی غلط املایی یا اشتباه تایپی دیدین اطلاع بدین.