برنامه نویسی

NestJS API را با استفاده از Typescript، MongoDB، Docker، Docker Compose ایجاد کنید

بسیار خوب، بیایید یک API پشتیبان CRUD ساده برای برنامه شما با استفاده از موارد زیر ایجاد کنیم:

  • NestJS

  • MongoDB

  • داکر

  • Docker Compose

  • مانگوس

  • تایپ اسکریپت

چیزی که یاد می گیرید

در این آموزش یاد خواهید گرفت که چگونه با استفاده از Nestjs به عنوان باطن اصلی، MongoDB به عنوان پایگاه داده، Mongoose به عنوان سرویس گیرنده پایگاه داده برای اتصال با NestJS، Docker و Docker Compose به عنوان اجراکننده برنامه خود در کانتینر، یک API پایه ایجاد کنید.

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

معماری

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

ایجاد اپلیکیشن

ابتدا باید برنامه پایه خود را با استفاده از NestJS ایجاد کنیم. شروع به ایجاد کد دیگ بخار با اجرا کنید

npx @nestjs/cli new nestjs-backend-api
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

این دستور کلید NestJS را برای ایجاد یک شروع کننده برنامه Nest فعال می کند. Nest همچنین قبلاً برخی از وابستگی‌ها را برای برنامه نصب کرده است. پس از اتمام نصب، یک ثانیه صبر کنید.

وابستگی ها را نصب کنید

بیایید برخی از وابستگی ها را برای پشتیبانی از برنامه نصب کنیم. من توضیح خواهم داد که چه چیزی و چرا باید این وابستگی را در برنامه خود نصب کنید.

اکنون این دستور را در ترمینال برنامه خود اجرا کنید.

npm i -S @nestjs/config @nestjs/mongoose @nestjs/swagger @nestjs/throttler class-transformer class-validator compression helmet mongoose
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

با اجرای دستور بالا چندین وابستگی از جمله:

  • @nestjs/config – اجازه حل کردن پیکربندی از جمله استفاده از فایل env

  • @nestjs/mongoose – ماژول mongoose در nestjs

  • @nestjs/swagger – به NestJS اجازه دهید از OpenAPI Specification استفاده کند و از نوع نقشه برداری شده از جمله جزئی، حذف و غیره استفاده کند.

  • @nestjs/throttler – ماژول محدود کننده نرخ برای کاربرد

  • کلاس ترانسفورماتور، اعتبار سنجی کلاس – برای مدیریت تبدیل و اعتبار سنجی شیء برنامه استفاده می شود.

  • فشرده سازی – اجازه دهید تا پاسخ بدن از طریق Gzip فشرده شود

  • کلاه ایمنی – محافظت از برنامه برای جلوگیری از خشونت بی رحم XSS

  • مونگوس – استفاده از اتصال MongoDB به برنامه

اسکریپت پروژه را به روز کنید

اسکریپت را به روز کنید تا واضح تر شود. ما باید پیش نمایش را تغییر دهیم و شروع به استفاده از یک دستور جدید کنیم.

"scripts": {
    "build": "nest build",
    "format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
    "preview": "nest start",
    "dev": "nest start --watch",
    "debug": "nest start --debug --watch",
    "start": "node dist/main",
    "lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
    "test": "jest",
    "test:watch": "jest --watch",
    "test:cov": "jest --coverage",
    "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
    "test:e2e": "jest --config ./test/jest-e2e.json"
},
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

پس از تغییر به این شی، اکنون می توانید به راحتی برنامه ها را با استفاده از آن اجرا کنید

  • برنامه npm را اجرا کنید برای اجرا بر روی سرور توسعه

  • پیش نمایش اجرا npm اجرای برنامه پیش ساخته برای پیش نمایش

  • npm شروع اجرا برای اجرای برنامه در حالت تولید

برنامه را بوت استرپ کنید

حالا بیایید به داخل بپریم src/main.ts فایل. سپس با استفاده از این کد کد را تغییر دهید.

import { NestFactory } from '@nestjs/core'
import { AppModule } from './app.module'
import { ValidationPipe, VersioningType } from '@nestjs/common'
import helmet from 'helmet'
import * as compression from 'compression'

const PORT = parseInt(process.env.PORT, 10) || 4000

async function bootstrap() {
  const app = await NestFactory.create(AppModule)

  // register all plugins and extension
  app.enableCors({ origin: '*' })
  app.useGlobalPipes(new ValidationPipe({}))
  app.enableVersioning({ type: VersioningType.URI })
  app.use(helmet())
  app.use(compression())

  await app.listen(PORT, () => {
    console.log(`🚀 Application running at port ${PORT}`)
  })
}

bootstrap()
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

به من اجازه می دهد توضیح دهم ابتدا باید تمام وابستگی ها و تنظیمات را وارد کنیم. سپس شروع به استفاده از برخی از میان افزارها از جمله فشرده سازی و کلاه ایمنی کنید. سپس منابع اشتراک گذاری متقاطع را فعال کنید. همچنین می توانید تنظیمات Cors را تغییر دهید.

فعال کردن لوله اعتبار سنجی جهانی، با استفاده از اعتبارسنجی جهانی، ما به طور خودکار از کلاس class-transform و class-validator استفاده می کنیم. هنگامی که خطایی در شی اتفاق می افتد، خطا به عنوان یک پاسخ نشان داده می شود.

با استفاده از نوع URI، نسخه‌سازی را فعال کنید. شما به راحتی می توانید نسخه را در کنترلر مدیریت کنید. این روش به شما کمک می کند تا تغییراتی را در API خود بدون تأثیر نسخه زیر اضافه کنید.

ماژول برنامه را بپیچید

برای اطمینان از اجرای همه برنامه‌ها، باید همه ماژول‌های ویژگی و ماژول دیگری را به ماژول برنامه وارد کنید. زیرا اگر در main.ts فایل بهترین برنامه ایجاد برنامه از AppModule است.

ماژول برنامه را با استفاده از این کد تغییر دهید

import { Module } from '@nestjs/common'
import { AppController } from './app.controller'
import { ConfigModule } from '@nestjs/config'
import { ThrottlerModule } from '@nestjs/throttler'
import { UserModule } from './user/user.module'
import { MongooseModule } from '@nestjs/mongoose'

@Module({
  imports: [
    ConfigModule.forRoot({ isGlobal: true }),
    ThrottlerModule.forRoot({ limit: 10, ttl: 60 }),
    MongooseModule.forRoot(process.env.DATABASE_URI, {
      dbName: process.env.DATABASE_NAME,
      auth: {
        username: process.env.DATABASE_USER,
        password: process.env.DATABASE_PASS,
      },
    }),

    // feature module
    UserModule,
  ],
  controllers: [AppController],
})
export class AppModule {}
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

ابتدا کلاس را با ماژول دکوراتور تعریف می کنیم و خصوصیات داخل آن را تعریف می کنیم. داخل import برای وارد کردن ماژول برنامه استفاده می شود. از، ماژول محلی شما یا از وابستگی ها.

بنابراین ابتدا اجازه دهید ماژول Config را وارد کنیم، به یاد داشته باشید که isGlobal روی true تنظیم شده است. این به شما امکان می دهد از پیکربندی، محیط و تنظیمات خود در همه حوزه های برنامه خود استفاده کنید.

ماژول Throttler را اضافه کنید و حد و ttl را بدهید. این API باطن ما را از حملات brute-force محافظت می کند. کاربر فقط تا 10 بار اجازه دسترسی به همان منبع API را می دهد، سپس یک دقیقه صبر کنید تا امتحان کنید. اگر در این مورد تعجب می کنید، می توانید در مورد امنیت حمله با نیروی brute force اطلاعات بیشتری کسب کنید.

ماژول Mongoose را برای اتصال به MongoDB نصب کنید. لطفاً URI و احراز هویت را با استفاده از نام کاربری و رمز عبور مشخص کنید. بعداً با استفاده از فایل .env این موضوع را پوشش خواهم داد. پس با ما همراه باشید.

ماژول ویژگی را نصب کنید، بعداً به این موضوع خواهیم پرداخت.

ایجاد ویژگی کاربر

با رفتن به ویژگی کاربر، ما نیاز به راه اندازی و ایجاد یک ماژول جدید به نام داریم. کاربر. در داخل ویژگی کاربر، ما اطلاعات کاربر از جمله نام کاربری، رمز عبور، بیوگرافی، ایمیل و نام کامل را مدیریت خواهیم کرد.

در این ماژول، ما عملیات CRUD aka (ایجاد، خواندن، به‌روزرسانی، حذف) را برای کاربر مدیریت می‌کنیم. پس با این گفته بیایید وارد آن شویم.

درون src پوشه ایجاد یک پوشه جدید به نام کاربر. همه فایل های ماژول شما از جمله طرحواره، سرویس، ارائه دهنده و مدل در داخل این پوشه.

پس یک فایل جدید به نام بسازید user.schema.ts . داخل پوشه model سپس این کد را داخل آن قرار دهید

import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose'
import { HydratedDocument } from 'mongoose'

export type UserDocument = HydratedDocument<User>

@Schema({ collection: 'users', timestamps: true })
export class User {
  @Prop()
  fullName: string

  @Prop()
  email: string

  @Prop()
  bio: string

  @Prop()
  password: string
}

export const UserSchema = SchemaFactory.createForClass(User)
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

این فایل که برای مدیریت مجموعه کاربران در MongoDB استفاده می شود. استفاده از timestamp به ما این امکان را می دهد که به طور خودکار ویژگی های createAt و updatedAt را در Mongodb اضافه کنیم. بعد از اینکه مدل تعریف شد. ما باید طرح کاربر را با استفاده از schema factory از Mongoose ایجاد کنیم.

یک فایل جدید به نام user.input.ts در داخل پوشه مدل ایجاد کنید. سپس این کد را داخل آن قرار دهید.

import { OmitType } from '@nestjs/swagger'
import { IsEmail, IsString } from 'class-validator'

export class CreateUserInput {
  @IsString()
  fullName: string

  @IsEmail()
  email: string

  @IsEmail()
  bio: string

  @IsString()
  password: string
}

export class UpdateUserInput extends OmitType(CreateUserInput, [
  'password'
] as const) {}
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

این مدل مخصوص استفاده از ورودی از کنترلر است. بنابراین همه ورودی ها را در یک مکان تنظیم می کنیم. فراموش نکنید که قبل از پردازش کاربر از اعتبارسنجی کلاس برای تأیید اعتبار ورودی استفاده کنید.

این UpdateUserInput نوع حذف از را گسترش می دهد CreateUserInput کلاس این از @nestjs/swagger. همانطور که گفتم می توانید از همان کلاس استفاده کنید و نوع را حذف کنید تا خاصیت خاص را حذف کنید. بنابراین لازم نیست آن را دو بار ایجاد کنید. بعداً می‌توانید درباره Swagger بیشتر کاوش کنید.

UserPayload را ایجاد کنید

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

import { PartialType } from '@nestjs/swagger'
import { User } from './user.schema'

export class UserPayload extends PartialType(User) {
  createdA?: string
  updateAt?: string
}
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

همانطور که گفتم ما از @nestjs/swagger بسته اینجا PartialType به ما امکان می دهد از همان شیء برای گسترش آن استفاده کنیم.

ایجاد سرویس کاربری

یک فایل جدید به نام ایجاد کنید user.service.ts سپس این کد را داخل فایل قرار دهید. این یک منطق جدید برای مدیریت کاربر ایجاد می کند.

import { Injectable, NotFoundException } from '@nestjs/common'
import { InjectModel } from '@nestjs/mongoose'
import { User } from './model/user.schema'
import { Model } from 'mongoose'
import { CreateUserInput, UpdateUserInput } from './model/user.input'
import { UserPayload } from './model/user.payload'

@Injectable()
export class UserService {
  constructor(@InjectModel(User.name) private userModel: Model<User>) {}

  async createUser(body: CreateUserInput): Promise<UserPayload> {
    const createdUser = new this.userModel(body)
    const user = await createdUser.save()
    return user
  }

  async findUser(id: string): Promise<UserPayload> {
    const user = await this.userModel.findOne({ _id: id }).exec()

    if (!user) {
      throw new NotFoundException(`User with email id:${id} not found `)
    }
    return user
  }

  async listUser(): Promise<UserPayload[]> {
    const users = await this.userModel.find()
    return users
  }

  async updateUser(id: string, body: UpdateUserInput): Promise<UserPayload> {
    await this.userModel.updateOne({ _id: id }, body)
    const updatedUser = this.userModel.findById(id)
    return updatedUser
  }

  async deleteUser(id: string): Promise<void> {
    await this.userModel.deleteOne({ _id: id })
  }
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

این سرویس عملیات CRUD را برای کاربران انجام می دهد، از جمله ایجاد یک کاربر جدید، فهرست کردن همه کاربران، خواندن جزئیات، حذف و به روز رسانی. در داخل سازنده سرویس، ما را تزریق می کنیم UserModel استفاده كردن UserSchema.

کنترل کننده کاربر ایجاد کنید

تمام نقاط پایانی API، مسیرها، نسخه‌ها و روش‌ها در اینجا قرار می‌گیرند. حال یک فایل جدید به نام ایجاد کنید user.controller.ts داخل پوشه کاربر سطح بالا سپس این کد را داخل آن قرار دهید.

import { Body, Controller, Delete, Get, Param, Post, Put } from '@nestjs/common'
import { CreateUserInput } from './model/user.input'
import { UserService } from './user.service'

@Controller({
  path: 'users',
  version: '1',
})
export class UserController {
  constructor(private readonly userService: UserService) {}

  @Post()
  createUser(@Body() body: CreateUserInput) {
    return this.userService.createUser(body)
  }

  @Get('/list')
  listUser() {
    return this.userService.listUser()
  }

  @Get('/:id')
  findUser(@Param('id') id: string) {
    return this.userService.findUser(id)
  }

  @Put('/:id')
  updateUser(@Param('id') id: string, @Body() body: CreateUserInput) {
    return this.userService.updateUser(id, body)
  }

  @Delete('/:id')
  deleteUser(@Param('id') id: string) {
    return this.userService.deleteUser(id)
  }
}
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

ماژول کاربر ایجاد کنید

اکنون همه کدها را وارد کرده و آن را داخل ماژول کاربر قرار دهید. یک فایل جدید به نام ایجاد کنید user.module.ts داخل کاربر. سپس این کد را داخل آن قرار دهید

import { Module } from '@nestjs/common'
import { UserService } from './user.service'
import { UserController } from './user.controller'
import { MongooseModule } from '@nestjs/mongoose'
import { User, UserSchema } from './model/user.schema'

@Module({
  imports: [
    MongooseModule.forFeature([{ name: User.name, schema: UserSchema }]),
  ],
  providers: [UserService],
  controllers: [UserController],
})
export class UserModule {}
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

فراموش نکنید که طرح MongoDB را با استفاده از ثبت نام کنید forFeature. سپس کنترلر و سرویس را وارد کنید.

ایجاد محیط

حالا بیایید یک فایل جدید به نام ایجاد کنیم env داخل پوشه root سپس این کد را داخل آن قرار دهید.

# APPS CONFIG
PORT = # YOUR_DATABASE_PORT

# DATABASE CONFIGS
DATABASE_NAME = # YOUR_DATABASE_NAME
DATABASE_USER = # YOUR_DATABASE_USER
DATABASE_PASS = # YOUR DATABASE_PASS
DATABASE_URI = # YOUR_DATABASE_URI, example: mongodb://localhost:2701
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

این محیط را با استفاده از ارزش خود تغییر دهید.

برنامه را داکر کنید

ما باید پایگاه داده خود را با استفاده از docker تنظیم کنیم. بنابراین باید از اجرای صحیح برنامه اطمینان حاصل کنیم.

Dockerfile ایجاد کنید

بیایید یک فایل جدید به نام ایجاد کنیم Dockerfile در پوشه root، سپس این کد را داخل آن قرار دهید.

# Application Docker file Configuration
# Visit https://docs.docker.com/engine/reference/builder/
# Using multi stage build

# Prepare the image when build
# also use to minimize the docker image
FROM node:14-alpine as builder

WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build


# Build the image as production
# So we can minimize the size
FROM node:14-alpine

WORKDIR /app
COPY package*.json ./
ENV PORT=4000
ENV NODE_ENV=Production
RUN npm install
COPY --from=builder /app/dist ./dist
EXPOSE ${PORT}

CMD ["npm", "run", "start"]
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

این فایل برای پیکربندی برنامه ما برای تبدیل شدن به یک تصویر داکر استفاده خواهد شد. هنگام اجرای docker-compose. سرویس یک تصویر جدید از برنامه باطن ما ایجاد می کند. و سپس آن را به صورت ظرف اجرا کنید.

مفهوم فقط کپی کردن است package.json و package-lock.json فایل را در پوشه کاری قرار دهید، سپس deps را نصب کنید. دستور با اجرا کردن برنامه را می سازد npm run build، سپس تمام پوشه های dist را در آخرین سازنده تصویر کپی کنید. حداقل یک را نیز اضافه می کنیم env فایل برای PORT. بنابراین می توانید پورت را به صورت پویا و بدون شکستن برنامه تغییر دهید.

Docker Compose را ایجاد کنید

Docker Compose به شما این امکان را می دهد که تمام سرویس ها و کانتینرهای در حال اجرا در رایانه خود را راحت تر مدیریت کنید. نوشتن به شما امکان می دهد سرویس، تصویر و شبکه را در یک مکان بدون نگرانی در مورد به خاطر سپردن دستور در ترمینال خود تعریف کنید. به همین دلیل است که توصیه می کنم به جای استفاده از دستورات دستی برای اجرای کانتینر، از docker-compose استفاده کنید.

یک فایل جدید به نام ایجاد کنید .docker-compose.yaml داخل پوشه روت خود، سپس این کد را داخل آن قرار دهید.

# Docker Compose Configuration
# visit https://docs.docker.com/compose/

version: '3.8'
services:
  # app service for your backend
  app:
    container_name: backend
    build:
      context: ./
      dockerfile: Dockerfile
    environment:
      DATABASE_NAME: # DATABASE_NAME
      DATABASE_USER: # DATABASE_USER
      DATABASE_PASS: # DATABASE_PASS
      DATABASE_URI: # DATABASE_URI, example: mongodb://database:27017
    ports:
      - '4000:4000'
    depends_on:
      - database

  # start the mongodb service as container
  database:
    image: mongo:6.0
    container_name: mongodb
    restart: always
    ports:
      - '27017:27017'
    environment:
      MONGO_INITDB_ROOT_USERNAME: # DATABASE_NAME
      MONGO_INITDB_ROOT_PASSWORD: # DATABASE_USER
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

این نوشتن دو سرویس به نام برنامه و پایگاه داده ایجاد می کند. در داخل برنامه، می توانید آن را بدون یافتن تصویر مشاهده کنید. با این حال، ما زمینه ای را پیدا کردیم که به Dockerfile اشاره دارد. این به کاربر امکان می دهد یک تصویر جدید برای سرویس ایجاد کند.

در کنار مونگو، از آن آمده است داکرهاب تصویر با نسخه 6.0. همچنین می توانید هر متغیر و محیطی را برای تصویر از جمله نام کاربری و رمز عبور تعریف کنید.

توجه: هنگام اتصال پایگاه داده با استفاده از شبکه docker در برنامه، دیگر به لوکال هاست دسترسی نخواهید داشت، بلکه با استفاده از نام سرویس مانند پایگاه داده دسترسی خواهید داشت. به عنوان مثال اگر در محلی می توانید با استفاده از پایگاه داده به پایگاه داده متصل شوید mongodb://localhost:27017، اما پس از استفاده از docker باید از نام سرویس مانند استفاده کنید mongodb://database:27017 . این تنها کاری است که شما شبکه جدید را برای پایگاه داده در معرض نمایش قرار نمی دهید.

api backend را اجرا کنید

برای اجرای این برنامه می توانید با استفاده از این دستور اجرا کنید.

docker-compose up -d
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

این دستور docker compose را برای اجرای همزمان 2 سرویس فعال می کند، همچنین تصویر را می کشد و به عنوان محفظه docker تنظیم می کند. برای دسترسی به برنامه خود، می توانید تایپ کنید http://localhost:4000/v1/users.

کلمات اخر

قبل از پرش لطفاً کاوش کنید و سعی کنید با خودتان کدنویسی کنید یا جریان را درک کنید. همه این کدها در مخزن من قرار داده شده است.

نوشته های مشابه

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *

دکمه بازگشت به بالا