۲۹ شهریور ۱۴۰۳

Techboy

اخبار و اطلاعات روز تکنولوژی

معرفی Nest.js: سرور جاوا اسکریپت و تایپ اسکریپت درجه بالاتر

Nest.js برای پارادایم های برنامه نویسی مدرن و رویکرد ماژولار به جاوا اسکریپت و TypeScript سمت سرور می درخشد. در اینجا یک معرفی عملی برای Nest است.

Nest.js برای پارادایم های برنامه نویسی مدرن و رویکرد ماژولار به جاوا اسکریپت و TypeScript سمت سرور می درخشد. در اینجا یک معرفی عملی برای Nest است.

نباید با Next.js اشتباه گرفته شود، Nest.js یک رویکرد جدیدتر و منحصر به فرد برای فناوری سرور جاوا اسکریپت است. این یک سرور آشنا مانند Express یا Fastify را می‌گیرد و تعدادی انتزاع مفید را لایه‌بندی می‌کند، که در جهت توانمندسازی و ساده‌سازی طراحی برنامه‌های سطح بالاتر هستند. به لطف ترکیب متمایز پارادایم های برنامه نویسی، پشتیبانی از نوع اسکریپت مرتبه اول و ویژگی های داخلی مانند تزریق وابستگی، Nest.js در چند سال گذشته به طور پیوسته محبوبیت خود را افزایش داده است.

Nest.js کمک جالبی به اکوسیستم جاوا اسکریپت است و ارزش توجه شما را دارد. این یک ابزار عالی است که هنگام کار با جاوا اسکریپت و TypeScript سمت سرور باید به خاطر داشته باشید.

نمای کلی

در این مقاله، تور گردبادی Nest.js را با مثال‌هایی از جمله مسیریابی، کنترل‌کننده‌ها، تولیدکنندگان (تزریق وابستگی) و احراز هویت با محافظ‌ها انجام می‌دهیم. همچنین درک درستی از سیستم ماژول Nest.js خواهید داشت.

مثال ما یک برنامه کاربردی است که برای مدیریت لیستی از دستور العمل های پاستا استفاده می شود. ما یک سرویس وابستگی تزریق می کنیم که مجموعه داده واقعی را مدیریت می کند و یک API RESTful که می توانیم از آن برای فهرست کردن همه دستور العمل ها یا بازیابی یک دستور غذا بر اساس شناسه استفاده کنیم. ما همچنین یک نقطه پایانی ساده تأیید شده PUT برای افزودن دستور العمل های جدید راه اندازی می کنیم.

بیایید با داربست یک پروژه جدید شروع کنیم. پس از آن، می‌توانیم نمونه‌ها را بررسی کنیم.

نمونه نمایشی Nest.js را راه اندازی کنید

ما می‌توانیم از رابط خط فرمان Nest.js برای راه‌اندازی طرح‌بندی سریع برنامه استفاده کنیم، که با نصب جهانی Nest با این موارد شروع می‌شود: $ npm install -g @nestjs/cli. علاوه بر دستور create، nestjs دارای ویژگی‌های مفیدی مانند generate برای اشتراک‌گذاری طرح‌های قابل استفاده مجدد است. نصب جهانی به ما امکان دسترسی به آن و موارد دیگر را می دهد.

اکنون می‌توانیم یک برنامه کاربردی جدید با: $ nest new iw-nest ایجاد کنیم. می‌توانید مدیر بسته‌ای را که می‌خواهید انتخاب کنید (npm، yarn یا pnpm). برای این نسخه نمایشی، از pnpm استفاده خواهم کرد. فرآیند بدون توجه به یکسان است.

به دایرکتوری جدید /iw-nest تغییر دهید و سرور توسعه دهنده را با: $ pnpm run start راه اندازی کنید. با مراجعه به localhost:3000 می‌توانید تأیید کنید که برنامه در حال اجرا است، جایی که باید «سلام، جهان» را ببینید. پیام این پیام از iw-nest/src/app.controller.ts می آید. اگر به آن فایل نگاه کنید، می بینید که از یک سرویس تزریقی استفاده می کند. بیایید یک کنترلر جدید ایجاد کنیم (src/recipes.controller.ts) که فهرستی از دستور العمل ها را برمی گرداند، همانطور که در فهرست ۱ نشان داده شده است.


import { Controller, Get, Inject } from '@nestjs/common';

@Controller('recipes')
export class RecipesController {
  @Get()
  getRecipes() {
    return '[{"name":"Ravioli"}]';
  }
}

مسیریابی با کنترلرها

فهرست ۱ به ما نگاهی به اصول مسیریابی در Nest.js می دهد. می بینید که ما از حاشیه نویسی @Controller('recipes') برای تعریف کلاس به عنوان یک کنترلر با مسیر /recipes استفاده می کنیم. روش getRecipes() برای مدیریت روش GET با @Get() حاشیه نویسی شده است.

Pulumi زیرساخت را به عنوان پشتیبانی کد برای جاوا و YAML گسترش می دهد

در حال حاضر، این کنترل‌کننده به سادگی /recipes GET را به یک رشته پاسخ با کد سخت نگاشت می‌کند. قبل از اینکه Nest.js این مورد را ارائه دهد، باید کنترلر جدید را با ماژول ثبت کنیم. ماژول ها مفهوم مهم دیگری در Nest هستند که برای کمک به سازماندهی کد برنامه شما استفاده می شوند. در مورد ما، باید /src/app.module.ts را باز کنیم و کنترلر را مانند فهرست ۲ اضافه کنیم.


import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
// Our new controller:
import { RecipesController } from './recipes.controller'; 

@Module({
  imports: [],
  controllers: [AppController,RecipesController],
  providers: [AppService],
})
export class AppModule {}

چارچوب تزریق وابستگی در Nest.js یادآور Spring در اکوسیستم جاوا است. داشتن تزریق وابستگی داخلی به تنهایی باعث می‌شود Nest.js حتی بدون سایر زنگ‌ها و سوت‌هایش ارزش بررسی داشته باشد.

ما یک ارائه‌دهنده خدمات تعریف می‌کنیم و آن را به کنترل‌کننده خود سیم‌کشی می‌کنیم. این یک روش تمیز برای سازماندهی برنامه در لایه ها است. می‌توانید کلاس خدمات جدید ما، /src/recipes.service.ts را در فهرست ۳ ببینید.


import { Injectable } from '@nestjs/common';

@Injectable()
export class RecipesService {
  private readonly recipes = [
    {
      name: 'Ravioli',
      ingredients: ['pasta', 'cheese', 'tomato sauce'],
    },
    {
      name: 'Lasagna',
      ingredients: ['pasta', 'meat sauce', 'cheese'],
    },
    {
      name: 'Spaghetti',
      ingredients: ['pasta', 'tomato sauce'],
    },
  ];

  getRecipes() {
    return this.recipes;
  }
}

سرویس را به ماژول اضافه کنید

برای استفاده از این ارائه دهنده خدمات، باید آن را به فایل app.module.ts نیز اضافه کنیم، همانطور که در فهرست ۴ نشان داده شده است.


@Module({
  imports: [],
  controllers: [AppController,RecipesController],
  providers: [AppService, RecipesService] // add the service 
})

ماژول ها راه خوبی برای سازماندهی برنامه هستند. آنها می توانند به عنوان یک مکانیسم گروه بندی منطقی عمل کنند و یک ساختار سلسله مراتبی ارائه دهند که در آن اساسی ترین ماژول ها به وضوح تعریف شده اند و بقیه به آنها بستگی دارند.

استفاده از سرویس

اکنون می‌توانیم از این سرویس در RecipesController استفاده کنیم، همانطور که در فهرست ۵ نشان داده شده است. اما توانایی تعریف و مصرف کلاس‌ها در سطح برنامه، به روشی استاندارد، می‌تواند یک موهبت واقعی برای معماری برنامه شما با رشد سیستم باشد.


import { Controller, Get, Inject } from '@nestjs/common';
import { RecipesService } from './recipes.service';

@Controller('recipes')
export class RecipesController {
  @Inject()
  private readonly recipesService: RecipesService;
  @Get()
  getRecipes() {
    return this.recipesService.getRecipes();
  }
}

در اصل، کلاس RecipesService را وارد می کنیم، سپس برای دریافت ارجاع به آن، از حاشیه نویسی @Inject() در recipesService عضو. سیستم تزریق این را بر اساس نوع به نمونه ای از کلاس RecipesService متصل می کند. به‌طور پیش‌فرض، سرویس‌های تزریق‌شده سینگلتون‌ها در Nest هستند، بنابراین همه کلاس‌های کلاینت مرجع مشابهی را دریافت خواهند کرد. نمونه، مثال. می‌توان از «محدوده‌های» دیگر برای سرویس‌ها برای تنظیم دقیق نحوه نمونه‌سازی آنها استفاده کرد. علاوه بر تزریق سازنده، Nest از تزریق مبتنی بر ویژگی پشتیبانی می‌کند.

اکنون، اگر برنامه را اجرا کنید و به localhost:3000/recipes بروید، خروجی JSON را از آرایه دستورهای موجود در سرویس خواهید دید.

ایجاد یک نقطه پایانی POST

اکنون، بیایید یک نقطه پایانی POST جدید اضافه کنیم تا به کاربران اجازه دهیم دستور العمل ها را اضافه کنند. ما آن را با ساده‌ترین احراز هویت ممکن با استفاده از چیزی که نگهبان در Nest.js نامیده می‌شود، ایمن می‌کنیم. یک نگهبان جلوی کنترلرها می نشیند و نحوه ارسال درخواست ها را تعیین می کند. می‌توانید محافظ ساده ما را در فهرست ۶ ببینید. در حال حاضر، فقط بررسی می‌کند که آیا سرصفحه احراز هویت در درخواست وجود دارد یا خیر.


import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common';

@Injectable()
export class AuthGuard implements CanActivate {
  canActivate(context: ExecutionContext): boolean {
    // Simple authentication logic
    const request = context.switchToHttp().getRequest();
    const authHeader = request.headers.authorization;
    return authHeader === 'Bearer secret-token';
  }
}

بعد، همانطور که در فهرست ۷ نشان داده شده است، نگهبان را با ماژول ثبت کنید.


// ...
import { AuthGuard } from './auth.guard';

@Module({
  imports: [],
  controllers: [AppController, RecipesController],
  providers: [AppService, RecipesService, AuthGuard], 
})
export class AppModule {}

اکنون می‌توانیم از سرویس نگهبان جدید برای محافظت از نقطه پایانی POST خود که در فهرست ۸ نشان داده شده است استفاده کنیم. به واردات جدید توجه کنید.


import { Controller, Get, Inject, Post, Body, UseGuards } from '@nestjs/common';
import { RecipesService } from './recipes.service';
import { AuthGuard } from "./auth.guard";

@Controller('recipes')
export class RecipesController {
  @Inject()
  private readonly recipesService: RecipesService;
  @Get()
  getRecipes() {
    return this.recipesService.getRecipes();
  }
  @Post()
  @UseGuards(AuthGuard)
  addRecipe(@Body() recipe: any) {
    return this.recipesService.addRecipe(recipe);
  }
}

توجه داشته باشید که حاشیه نویسی @UserGuards برای اعمال محافظ جدید در روش addRecipe() استفاده شده است، که به عنوان یک POST نقطه پایانی با حاشیه نویسی @Post. Nest از نمونه سازی و اعمال محافظ در نقطه پایانی برای ما مراقبت می کند.

ما همچنین یک روش addRecipe() را به سرویس اضافه کرده‌ایم که بسیار ساده است، همانطور که در فهرست ۹ نشان داده شده است.


addRecipe(recipe: any) {
    this.recipes.push(recipe);
    return recipe;
  }
  

اکنون می‌توانیم احراز هویت و نقطه پایانی را با چند درخواست CURL، مانند فهرست ۱۰، آزمایش کنیم.


$ curl -X POST -H "Authorization: Bearer secret-token" -H "Content-Type: application/json" -d '{"name": "Carbonara", "ingredients": ["pasta", "eggs", "bacon"]}' http://localhost:3000/recipes

{"name":"Carbonara","ingredients":["pasta","eggs","bacon"]}

$ curl -X POST -H "Content-Type: application/json" -d '{"name": "Carbonara", "ingredients": ["pasta", "eggs", "bacon"]}' http://localhost:3000/recipes

{"message":"Forbidden resource","error":"Forbidden","statusCode":403}

می‌توانید ببینید که مجوز کار می‌کند، زیرا فقط درخواستی که سربرگ حامل را نگه می‌دارد مجاز است.

استفاده از Nest با TypeScript

تا کنون، ما فقط از یک شی جاوا اسکریپت استفاده کرده ایم. در دنیای TypeScript، ایجاد یک شی مدل Recipe و استفاده از آن به عنوان یک شیء ارزش برای جابه‌جایی در اطراف اطلاعات رایج است. برای مثال، می‌توانیم کلاس Recipe (فهرست ۱۱) را ایجاد کنیم و از آن در روش addRecipe (فهرست ۱۲) استفاده کنیم.


export class Recipe {
  constructor(public name: string, public ingredients: string[]) {}

  getName(): string {
    return this.name;
  }

  getIngredients(): string[] {
    return this.ingredients;
  }
}

در نهایت، می توانید روش addRecipe() POST را به شدت تایپ کنید و Next به طور خودکار شی مدل را برای ما پر می کند:


import { Injectable } from '@nestjs/common';
import { Recipe } from './recipe.model';

@Injectable()
export class RecipesService {
  private readonly recipes: Recipe[] = [
    new Recipe('Ravioli', ['pasta', 'cheese', 'tomato sauce']),
    new Recipe('Lasagna', ['pasta', 'meat sauce', 'cheese']),
    new Recipe('Spaghetti', ['pasta', 'tomato sauce']),
  ];

  getRecipes(): Recipe[] {
    return this.recipes;
  }

  addRecipe(recipe: Recipe): Recipe {
    this.recipes.push(recipe);
    return recipe;
  }
}

سپس می توانید روش addRecipe() POST را به شدت تایپ کنید و Nest به طور خودکار شی مدل را برای ما پر می کند، همانطور که در فهرست ۱۳ نشان داده شده است.


// ...
import { Recipe } from './recipe.model';

@Controller('recipes')
export class RecipesController {
  // ...
  @Post()
  @UseGuards(AuthGuard)
  addRecipe(@Body() recipe: Recipe) {
    const newRecipe = new Recipe(recipe.name, recipe.ingredients);
    return this.recipesService.addRecipe(newRecipe);
  }
}

نتیجه گیری

انتخاب بین انعطاف‌پذیری تایپ اردک جاوا اسکریپت و تایپ قوی تایپ اسکریپت در واقع به چیزی بستگی دارد که شما، تیم یا سازمان تصمیم به استفاده از آن دارید. جاوا اسکریپت به شما سرعت توسعه و انعطاف‌پذیری می‌دهد، در حالی که TypeScript ساختار و پشتیبانی ابزار بیشتری را به شما می‌دهد.

همچنین شایان ذکر است که Nest.js از برنامه نویسی واکنشی استقبال می کند و می توانید وعده‌ها را از روش‌ها و نقاط پایانی بازگردانید. حتی بیشتر، می‌توانید RxJS Observable را برگردانید. این گزینه‌های قدرتمندی برای سیم‌کشی برنامه‌ها همراه با جریان‌های داده ناهمزمان در اختیار شما قرار می‌دهد.

اگرچه ما فقط سطح کاری را که می تواند انجام دهد خراشیده ایم، واضح است که Nest.js یک پلتفرم اندیشیده شده و توانا برای ساخت سرورهای Node است. این وعده یک لایه سطح بالاتر در بالای Express برای بهبود معماری و پشتیبانی طراحی را ارائه می دهد. اگر به دنبال ساخت جاوا اسکریپت سمت سرور و به‌ویژه برنامه‌های TypeScript هستید، Nest یک گزینه عالی است.