Nest课程:新手入门与初级教程

2024/11/13 23:03:13

本文主要是介绍Nest课程:新手入门与初级教程,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

概述

本文档详细介绍了基于TypeScript的Nest框架,该框架用于构建高效、可扩展的服务器端应用程序。文章通过环境搭建、项目初始化、核心概念、实战案例、数据库集成、常用功能和插件以及代码规范与测试等部分,全面讲解了Nest框架的应用和开发流程。

Nest框架简介
什么是Nest框架

Nest框架是一个基于TypeScript的高效、可扩展的JavaScript框架,主要用于构建服务器端应用程序。Nest框架的核心设计理念是遵循面向对象编程的原则,通过模块化、依赖注入和装饰器来构建可维护性高、易于扩展的应用程序。

Nest框架借鉴了多种现代软件开发的最佳实践,如Angular框架的设计理念,从而提供了一个强大的工具集来帮助开发者构建复杂的Web应用和服务。

Nest框架的特点与优势
  1. 模块化设计:Nest框架采用模块化的方式组织代码,每个功能模块都可以独立地开发、测试和部署。这种设计使得代码维护更加容易。
  2. 可扩展性:Nest框架的设计非常灵活,支持多种插件和扩展,开发者可以根据需要选择适合的中间件或插件来增强应用的功能。
  3. 依赖注入:依赖注入是一种设计模式,通过将组件的依赖项通过构造函数或属性注入,来实现高度解耦和测试友好的代码。Nest框架通过依赖注入的方式管理组件之间的依赖关系。
  4. 装饰器支持:装饰器是一种特殊类型的声明,可以附加到类声明、方法、访问器、属性等。Nest框架利用装饰器来定义路由、控制器、服务等,使得代码更加简洁、直观。
环境搭建与项目初始化

首先,确保计算机上安装了Node.js和npm。之后,可以通过Nest CLI来创建新的Nest项目。以下是安装Nest CLI和创建新项目的步骤:

安装Nest CLI

npm install -g @nestjs/cli

创建新项目

nest new my-nest-app

此命令将创建一个新的Nest项目,并安装必要的依赖项。my-nest-app是你的项目名称,你可以根据需要将其替换为你希望的名称。

初始化项目

nest new my-nest-app命令将自动完成基础设置,包括初始化package.jsonnest-cli.json文件。在项目目录中你可以看到以下结构:

my-nest-app/
├── src/
│   ├── app.module.ts
│   ├── main.ts
│   └── app.controller.spec.ts
├── test/
│   └── app.controller.spec.ts
├── .editorconfig
├── .gitignore
├── nest-cli.json
├── package.json
└── README.md

启动项目

启动项目只需要运行npm run start命令:

npm run start

这将启动开发服务器并监听3000端口。

基本概念与架构
控制器(Controllers)

控制器是Nest框架中处理HTTP请求的核心组件。控制器通过装饰器定义路由,并通过HTTP请求的方法(如GET、POST、PUT、DELETE)来处理相应的业务逻辑。

示例代码

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

@Controller('users')
export class UsersController {
  @Get()
  findAll(): string {
    return 'Find all users';
  }

  @Get(':id')
  findOne(@Param('id') id: string): string {
    return `Find user with id: ${id}`;
  }
}

路由配置

  • @Controller('users') 定义了控制器的路由前缀为/users
  • @Get()@Get(':id') 分别定义了HTTP GET请求的路由。
服务(Services)

服务是Nest框架中的业务逻辑处理组件。服务通常包含数据操作、逻辑处理等核心功能,并且可以通过依赖注入的方式在控制器中使用。

示例代码

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

@Injectable()
export class UsersService {
  private users = [];

  findAll(): string[] {
    return this.users;
  }

  findOne(id: string): string {
    return this.users.find(user => user.id === id);
  }
}
模块(Modules)

模块是Nest框架中的逻辑分组单元。一个模块可以包含控制器、服务、提供者和其他模块的引用。通过模块,可以将应用程序的不同部分进行抽象并组合在一起。

示例代码

import { Module } from '@nestjs/common';
import { UsersController } from './users.controller';
import { UsersService } from './users.service';

@Module({
  imports: [],
  controllers: [UsersController],
  providers: [UsersService],
})
export class UsersModule {}

模块配置

  • @Module 装饰器用于定义一个模块。
  • controllers 数组中定义了当前模块拥有的控制器。
  • providers 数组中定义了当前模块拥有的服务。
提供者(Providers)

提供者是Nest框架中的可注入的服务对象。提供者可以是任何具有业务逻辑的服务、拦截器、装饰器等。提供者在模块中通过@Injectable()修饰符定义,并在模块的providers数组中注册。

示例代码

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

@Injectable()
export class UserService {
  private users = [];

  findAll(): string[] {
    return this.users;
  }

  findOne(id: string): string {
    return this.users.find(user => user.id === id);
  }
}

注入提供者

提供者可以通过依赖注入的方式在控制器或其他服务中使用。例如:

import { Controller, Get } from '@nestjs/common';
import { UserService } from './user.service';

@Controller('users')
export class UsersController {
  constructor(private readonly userService: UserService) {}

  @Get()
  findAll(): string[] {
    return this.userService.findAll();
  }

  @Get(':id')
  findOne(@Param('id') id: string): string {
    return this.userService.findOne(id);
  }
}
实战:创建第一个Nest应用
定义路由与接口

在实际应用中,定义路由与接口是构建Web应用的基本步骤。Nest框架通过装饰器来定义路由和接口。

示例代码

import { Controller, Get, Post, Body } from '@nestjs/common';
import { CreateUserDto } from './dto/create-user.dto';
import { UsersService } from './users.service';

@Controller('users')
export class UsersController {
  constructor(private readonly usersService: UsersService) {}

  @Get()
  findAll(): string[] {
    return this.usersService.findAll();
  }

  @Get(':id')
  findOne(@Param('id') id: string): string {
    return this.usersService.findOne(id);
  }

  @Post()
  create(@Body() createUserDto: CreateUserDto): string {
    return this.usersService.create(createUserDto);
  }
}

路由配置

  • @Controller('users') 定义了控制器的路由前缀为/users
  • @Get()@Get(':id')@Post() 分别定义了HTTP GET和POST请求的路由。
实现简单的CRUD操作

接下来,我们将实现简单的CRUD操作。CRUD操作包括创建、读取、更新和删除记录。

示例代码

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

@Injectable()
export class UsersService {
  private users = [];

  findAll(): string[] {
    return this.users;
  }

  findOne(id: string): string {
    return this.users.find(user => user.id === id);
  }

  create(createUserDto: CreateUserDto): string {
    const user = {
      id: Date.now().toString(),
      ...createUserDto,
    };
    this.users.push(user);
    return 'User created successfully';
  }

  update(id: string, createUserDto: CreateUserDto): string {
    const user = this.users.find(user => user.id === id);
    if (!user) {
      return 'User not found';
    }
    Object.assign(user, createUserDto);
    return 'User updated successfully';
  }

  remove(id: string): string {
    const index = this.users.findIndex(user => user.id === id);
    if (index === -1) {
      return 'User not found';
    }
    this.users.splice(index, 1);
    return 'User deleted successfully';
  }
}

数据传输对象(DTO)

数据传输对象(DTO)用于定义数据结构。

export class CreateUserDto {
  name: string;
  age: number;
}

路由配置

@Controller('users')
export class UsersController {
  constructor(private readonly usersService: UsersService) {}

  @Get()
  findAll(): string[] {
    return this.usersService.findAll();
  }

  @Get(':id')
  findOne(@Param('id') id: string): string {
    return this.usersService.findOne(id);
  }

  @Post()
  create(@Body() createUserDto: CreateUserDto): string {
    return this.usersService.create(createUserDto);
  }

  @Put(':id')
  update(@Param('id') id: string, @Body() createUserDto: CreateUserDto): string {
    return this.usersService.update(id, createUserDto);
  }

  @Delete(':id')
  remove(@Param('id') id: string): string {
    return this.usersService.remove(id);
  }
}
使用装饰器与拦截器

装饰器和拦截器是Nest框架中非常强大的特性。装饰器可以修改类的行为,而拦截器则可以在请求的生命周期中添加额外的逻辑。

示例代码:装饰器

import { Controller, Get, Post, Body, Param } from '@nestjs/common';

@Controller('users')
export class UsersController {
  @Get('profile')
  @ProfileDecorator()
  profile(@Param('id') id: string): string {
    return `User profile for ${id}`;
  }
}

function ProfileDecorator() {
  return (target: any, key: string, descriptor: any) => {
    descriptor.value = (...args: any[]) => {
      console.log('Accessing user profile...');
      return descriptor.value.apply(this, args);
    };
  };
}

示例代码:拦截器

拦截器可以拦截请求的生命周期,并在请求的每个阶段添加额外的逻辑。

import { Injectable, NestInterceptor, ExecutionContext, CallHandler, HttpException, HttpStatus } from '@nestjs/common';
import { Observable } from 'rxjs';
import { tap } from 'rxjs/operators';

@Injectable()
export class LoggingInterceptor implements NestInterceptor {
  intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
    const req = context.switchToHttp().getRequest();
    const { method, url } = req;
    console.log(`[${method}] ${url}`);
    return next.handle().pipe(
      tap(() => console.log('Request completed')),
    );
  }
}

应用拦截器

import { Controller, Get, UseInterceptors } from '@nestjs/common';
import { LoggingInterceptor } from './logging.interceptor';

@Controller('users')
@UseInterceptors(LoggingInterceptor)
export class UsersController {
  @Get()
  findAll(): string[] {
    return [];
  }
}
数据库集成与ORM
连接数据库

在Nest框架中,常见的数据库集成方法是通过ORM(对象关系映射)工具。TypeORM是一个强大的ORM工具,支持多种数据库(如MySQL、PostgreSQL、SQLite、Oracle等)。以下是如何使用TypeORM连接数据库的步骤。

安装TypeORM

npm install --save @nestjs/typeorm typeorm mysql

配置数据库连接

app.module.ts文件中,配置TypeORM连接到数据库。

import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { UsersModule } from './users/users.module';

@Module({
  imports: [
    TypeOrmModule.forRoot({
      type: 'mysql',
      host: 'localhost',
      port: 3306,
      username: 'root',
      password: 'password',
      database: 'nest_db',
      entities: [__dirname + '/**/*.entity{.ts,.js}'],
      synchronize: true,
    }),
  ],
  imports: [UsersModule],
})
export class AppModule {}

定义实体(Entity)

在定义实体时,需要使用@Entity装饰器标记类为实体,并使用其他装饰器定义字段。

import { Entity, Column, PrimaryGeneratedColumn } from 'typeorm';

@Entity()
export class User {
  @PrimaryGeneratedColumn()
  id: number;

  @Column()
  name: string;

  @Column({ type: 'int' })
  age: number;
}
使用TypeORM进行数据操作

TypeORM提供了一种非常方便的方式来执行CRUD操作。在服务中,可以通过TypeORM的Repository来定义和执行数据库操作。

示例代码

import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { User } from './user.entity';

@Injectable()
export class UsersService {
  constructor(@InjectRepository(User) private userRepository: Repository<User>) {}

  async findAll(): Promise<User[]> {
    return this.userRepository.find();
  }

  async findOne(id: number): Promise<User> {
    return this.userRepository.findOne(id);
  }

  async create(user: User): Promise<User> {
    return this.userRepository.save(user);
  }

  async update(id: number, user: User): Promise<User> {
    return this.userRepository.update(id, user);
  }

  async remove(id: number): Promise<void> {
    await this.userRepository.delete(id);
  }
}

在控制器中调用服务

import { Controller, Get, Post, Body, Param } from '@nestjs/common';
import { UsersService } from './users.service';
import { User } from './user.entity';

@Controller('users')
export class UsersController {
  constructor(private usersService: UsersService) {}

  @Get()
  findAll(): Promise<User[]> {
    return this.usersService.findAll();
  }

  @Get(':id')
  findOne(@Param('id') id: number): Promise<User> {
    return this.usersService.findOne(id);
  }

  @Post()
  create(@Body() user: User): Promise<User> {
    return this.usersService.create(user);
  }

  @Put(':id')
  update(@Param('id') id: number, @Body() user: User): Promise<User> {
    return this.usersService.update(id, user);
  }

  @Delete(':id')
  remove(@Param('id') id: number): Promise<void> {
    return this.usersService.remove(id);
  }
}
数据库迁移与种子数据

在实际应用中,数据库迁移和种子数据是非常重要的步骤。TypeORM支持数据库迁移来管理数据库模式的变化。

安装迁移工具

npm install --save @nestjs/cli

创建迁移文件

nest g migration create-user-entity

执行迁移

nest migration:run

种子数据

可以通过脚本文件来添加初始数据。

import { EntityRepository, Repository } from 'typeorm';
import { User } from './user.entity';
import { Injectable } from '@nestjs/common';

@Injectable()
export class UsersRepository extends EntityRepository<User> {
  async seedData(): Promise<void> {
    const users = [
      { name: 'Alice', age: 25 },
      { name: 'Bob', age: 30 },
      { name: 'Charlie', age: 35 },
    ];

    for (const user of users) {
      await this.create(user);
    }
  }
}

执行种子数据

import { Injectable } from '@nestjs/common';
import { AppService } from './app.service';
import { UsersRepository } from './users/users.repository';

@Injectable()
export class AppSeeder {
  constructor(private appService: AppService, private usersRepository: UsersRepository) {}

  async seed(): Promise<void> {
    await this.usersRepository.seedData();
  }
}

在主函数中调用

import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { AppSeeder } from './app.seed';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  const appSeeder = app.get(AppSeeder);
  await appSeeder.seed();
  await app.listen(3000);
}
bootstrap();
常用功能与插件
日志记录与错误处理

日志记录和错误处理是每个应用程序的重要组成部分。Nest框架提供了多种内置的机制来实现日志记录和错误处理。

示例代码:全局异常拦截器

import { Injectable, NestInterceptor, ExecutionContext, CallHandler, HttpException, HttpStatus } from '@nestjs/common';
import { Observable } from 'rxjs';
import { tap } from 'rxjs/operators';

@Injectable()
export class LoggingInterceptor implements NestInterceptor {
  intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
    const req = context.switchToHttp().getRequest();
    const { method, url } = req;
    console.log(`[${method}] ${url}`);
    return next.handle().pipe(
      tap(() => console.log('Request completed')),
    );
  }
}

@Injectable()
export class UnhandledExceptionFilter implements ArgumentsHost {
  intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
    return next.handle().pipe(
      catchError((err: any) => {
        console.error('Unhandled Exception:', err);
        throw new HttpException(err, HttpStatus.INTERNAL_SERVER_ERROR);
      }),
    );
  }
}

全局异常拦截器注册

import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { UnhandledExceptionFilter } from './unhandled-exception.filter';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  app.useGlobalInterceptors(new LoggingInterceptor());
  app.useGlobalFilters(new UnhandledExceptionFilter());
  await app.listen(3000);
}
bootstrap();
缓存机制与中间件

缓存机制可以显著提高应用的性能,通过减少数据库和网络请求的次数,提高响应速度。Nest框架中可以使用中间件来实现缓存。

示例代码:缓存中间件

import { Injectable, NestMiddleware, MiddlewareConsumer, RequestMethod } from '@nestjs/common';
import { Request, Response, NextFunction } from 'express';

@Injectable()
export class CacheMiddleware implements NestMiddleware {
  use(req: Request, res: Response, next: NextFunction) {
    const cacheControl = 'max-age=3600, must-revalidate';
    res.setHeader('Cache-Control', cacheControl);
    res.setHeader('Pragma', 'no-cache');
    res.setHeader('Expires', '0');
    next();
  }
}

@Module({
  // ...
})
export class AppModule implements NestModule {
  configure(consumer: MiddlewareConsumer) {
    consumer
      .apply(CacheMiddleware)
      .forRoutes({ path: '*', method: RequestMethod.ALL });
  }
}
第三方插件的集成与使用

Nest框架通过依赖注入和模块化的方式,可以很容易地集成第三方插件。例如,可以使用@nestjs/axios来集成HTTP客户端,使用@nestjs/graphql来集成GraphQL。

示例代码:集成HTTP客户端

import { HttpService } from '@nestjs/axios';
import { Injectable } from '@nestjs/common';
import { AxiosResponse } from 'axios';

@Injectable()
export class ApiService {
  constructor(private httpService: HttpService) {}

  async getExternalData(): Promise<AxiosResponse<any>> {
    return await this.httpService.get('https://api.example.com/data').toPromise();
  }
}

配置模块

import { Module } from '@nestjs/common';
import { HttpModule } from '@nestjs/axios';
import { ApiService } from './api.service';

@Module({
  imports: [HttpModule],
  providers: [ApiService],
  exports: [ApiService],
})
export class ApiModule {}
代码规范与测试
代码风格与最佳实践

编写高质量的代码是开发过程中非常重要的一环。Nest框架提供了多种方式来帮助开发者编写规范的代码。

示例代码:代码风格检查

npm install --save-dev eslint eslint-config-prettier eslint-plugin-prettier prettier

配置.eslintrc.js

module.exports = {
  env: {
    browser: true,
    es2021: true,
    node: true,
  },
  extends: [
    'eslint:recommended',
    'google',
    'prettier',
    'prettier/@typescript-eslint',
  ],
  parser: '@typescript-eslint/parser',
  parserOptions: {
    ecmaFeatures: {
      tsx: true,
    },
    ecmaVersion: 12,
    sourceType: 'module',
  },
  plugins: ['@typescript-eslint', 'prettier'],
  rules: {
    'prettier/prettier': 'error',
    quotes: ['error', 'single'],
    semi: ['error', 'always'],
  },
};

使用Prettier进行格式化

module.exports = {
  ...tsSettings,
  plugins: ['prettier'],
  extends: ['google', 'prettier'],
  rules: {
    ...tsRules,
    '@typescript-eslint/semi': 'off',
    '@typescript-eslint/no-var-requires': 'off',
    'prettier/prettier': ['error', { semi: true }],
  },
};
单元测试与集成测试

单元测试和集成测试是确保代码质量的重要手段。Nest框架提供了丰富的测试工具,如jestsupertest

示例代码:单元测试

import { UsersService } from './users.service';
import { User } from './user.entity';

describe('UsersService', () => {
  let service: UsersService;

  beforeEach(() => {
    service = new UsersService();
  });

  it('should be defined', () => {
    expect(service).toBeDefined();
  });

  it('should return an array of users', () => {
    const users = service.findAll();
    expect(users).toEqual([]);
  });

  it('should create a new user', () => {
    const user = { name: 'Alice', age: 25 };
    const newUser = service.create(user);
    expect(newUser).toEqual(user);
  });
});

配置测试环境

npm install --save-dev jest @types/jest supertest

配置jest

module.exports = {
  preset: 'ts-jest',
  testEnvironment: 'node',
};
模拟与桩对象的使用

单元测试中经常需要模拟依赖对象,以确保测试的独立性和可重用性。Nest框架可以使用jest的模拟功能来实现这一目标。

示例代码:模拟依赖对象

import { UsersService } from './users.service';
import { UserService } from './user.service';
import { User } from './user.entity';

describe('UsersService', () => {
  let service: UsersService;
  let userService: jest.Mock<UserService>;

  beforeEach(() => {
    userService = {
      findAll: jest.fn().mockReturnValue([]),
      findOne: jest.fn().mockReturnValue({ id: 1, name: 'Alice', age: 25 }),
      create: jest.fn().mockResolvedValue({ id: 1, name: 'Alice', age: 25 }),
      update: jest.fn().mockResolvedValue({ id: 1, name: 'Alice', age: 25 }),
      remove: jest.fn().mockResolvedValue({ id: 1, name: 'Alice', age: 25 }),
    };
    service = new UsersService(userService);
  });

  it('should be defined', () => {
    expect(service).toBeDefined();
  });

  it('should return an array of users', () => {
    const users = service.findAll();
    expect(users).toEqual([]);
  });

  it('should create a new user', async () => {
    const user = { name: 'Alice', age: 25 };
    const newUser = await service.create(user);
    expect(newUser).toEqual({ id: 1, name: 'Alice', age: 25 });
  });
});

配置模拟

npm install --save-dev jest @types/jest

配置jest

module.exports = {
  preset: 'ts-jest',
  testEnvironment: 'node',
};
``

以上是Nest框架从基础到高级的全面介绍,涵盖了环境搭建、项目初始化、基本概念、实战示例、数据库集成、常用功能和插件、以及代码规范与测试。希望这些内容能够帮助你更好地理解和使用Nest框架。


这篇关于Nest课程:新手入门与初级教程的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程