为了账号安全,请及时绑定邮箱和手机立即绑定

Nest项目实战:新手入门教程

概述

本文详细介绍了如何从环境搭建到实战案例,一步步进行Nest项目实战,包括基础概念、数据库连接、用户管理功能的实现,以及如何进行测试和部署。通过这些步骤,开发者可以轻松构建和部署一个完整的用户管理系统。

1. Nest.js简介与环境搭建

1.1 什么是Nest.js

Nest.js 是一个使用 TypeScript 构建的高效、可扩展、结构化的Web应用程序框架。它基于 Angular 的设计模式,结合了面向对象编程、函数式编程及装饰器模式,旨在提供高度模块化的结构,使得开发者可以高效地开发复杂的Web应用。Nest.js 支持多种HTTP协议(如REST、GraphQL)和数据库(如MySQL、MongoDB),并且提供了丰富的中间件和工具库,方便开发者快速构建、测试和部署应用。

1.2 安装Node.js和NPM

要开始使用 Nest.js,首先需要安装Node.js和npm(Node.js的包管理器)。以下是安装步骤:

  1. 访问Node.js官网 https://nodejs.org/
  2. 点击“LTS”版本(长期支持版本),下载并安装。
  3. 安装完成后,打开命令行工具(如Windows的CMD或PowerShell,Mac和Linux的终端),检查Node.js和npm是否安装成功:
    node -v
    npm -v

    此命令会输出Node.js和npm的版本号,表示安装成功。

1.3 创建Nest.js项目

创建一个新的 Nest.js 项目可以通过 Nest CLI(命令行工具)来完成。以下是具体步骤:

  1. 安装Nest CLI:
    npm i -g @nestjs/cli
  2. 使用 Nest CLI 创建一个新的 Nest.js 项目:

    nest new my-nest-app

    这里my-nest-app是你项目的名称,你也可以用其他你喜欢的名字替换。

  3. 进入项目目录并安装依赖:
    cd my-nest-app
    npm install
  4. 运行项目:
    npm run start

    这将启动项目并在浏览器中打开http://localhost:3000/,你可以看到默认的“Hello World”页面。

1.4 安装与配置开发环境

为了进一步配置开发环境,可以安装一些常用的工具和库,例如:

  • TypeScript:Nest.js 主要使用 TypeScript 编写,因此确保你的项目中安装了 TypeScript。
    npm install --save-dev typescript
  • Prettier:代码格式化工具,有助于保持代码风格的一致性。
    npm install --save-dev prettier
  • ESLint:代码检查工具,帮助你发现和修复代码中的问题。
    npm install --save-dev eslint
  • Jest:单元测试框架,后续章节将详细介绍其使用方法。

安装完成后,参考 Nest.js 的官方文档进行相应的配置。

2. 第一个Nest.js应用

2.1 创建第一个控制器

在 Nest.js 中,控制器是用来处理 HTTP 请求的组件,每个控制器包含一组路由方法。这里我们将创建一个简单的控制器来响应HTTP GET 请求。

  1. 在命令行中生成一个新的控制器:

    nest generate controller welcome

    这将生成一个名为welcome.controller.ts的文件,位于项目根目录下的src文件夹中。

  2. 打开生成的控制器文件,并添加一个处理 GET 请求的方法:

    import { Controller, Get } from '@nestjs/common';
    
    @Controller('welcome')
    export class WelcomeController {
      @Get()
      getWelcomeMessage() {
        return 'Hello, Welcome to Nest.js!';
      }
    }
  3. 确保在src/app.module.ts文件中已经导入并注册了新创建的控制器:

    import { Module } from '@nestjs/common';
    import { AppController } from './app.controller';
    import { AppService } from './app.service';
    import { WelcomeController } from './welcome/welcome.controller';
    
    @Module({
      imports: [],
      controllers: [AppController, WelcomeController],
      providers: [AppService],
    })
    export class AppModule {}

2.2 定义路由与HTTP请求

在 Nest.js 中,路由是通过装饰器来定义的。上一节中,我们已经使用了@Get()装饰器定义了一个 GET 请求的路由。接下来,我们将添加一个 POST 请求的路由。

  1. welcome.controller.ts文件中添加一个处理 POST 请求的方法:

    @Post()
    postWelcomeMessage() {
      return 'This is a POST request to welcome endpoint.';
    }
  2. 确保在src/app.module.ts文件中,AppModule中已经注册了新的路由。
    @Module({
      imports: [],
      controllers: [AppController, WelcomeController],
      providers: [AppService],
    })
    export class AppModule {}

2.3 运行与测试应用

现在,我们已经创建并定义了路由,可以运行应用并测试新添加的路由。

  1. 在命令行中运行项目:

    npm run start
  2. 使用 Postman 或者浏览器测试新路由。例如,访问 http://localhost:3000/welcome 查看 GET 请求的响应,访问 http://localhost:3000/welcome?body=POST 查看 POST 请求的响应。

通过这种方式,你可以创建并测试更多的路由,以满足你的应用需求。

3. Nest.js核心概念

3.1 模块(Module)

模块是 Nest.js 中最重要的概念之一。每个模块都是一个独立的可重用组件,可以包含一组服务、控制器、提供者和其他模块。模块通过导入其他模块来实现依赖注入,从而建立一个松耦合、高效且可扩展的结构。

创建模块的过程如下:

  1. 在命令行中生成一个新的模块:

    nest generate module users

    这将生成一个名为users.module.ts的文件,位于项目根目录下的src文件夹中。

  2. 打开生成的模块文件并定义其内容:

    import { Module } from '@nestjs/common';
    import { UsersController } from './users.controller';
    import { UsersService } from './users.service';
    
    @Module({
      imports: [],
      controllers: [UsersController],
      providers: [UsersService],
    })
    export class UsersModule {}
  3. src/app.module.ts文件中导入并注册新创建的模块:
    @Module({
      imports: [UsersModule],
      controllers: [AppController],
      providers: [AppService],
    })
    export class AppModule {}

模块的定义包括importscontrollersproviders,分别用于导入其他模块、定义控制器和定义提供者。

3.2 服务(Service)

服务是 Nest.js 中的业务逻辑处理组件。服务的职责通常包括:数据处理、业务规则实现、与外部系统交互等。服务通常不直接与 HTTP 请求直接交互,而是由控制器来调用服务来处理请求。

创建服务的过程如下:

  1. 在命令行中生成一个新的服务:

    nest generate service users

    这将生成一个名为users.service.ts的文件,位于项目根目录下的src/users文件夹中。

  2. 打开生成的服务文件并定义其内容:

    import { Injectable } from '@nestjs/common';
    
    @Injectable()
    export class UsersService {
      getUsers() {
        return [
          { id: 1, name: 'John Doe' },
          { id: 2, name: 'Jane Doe' },
        ];
      }
    }
  3. users.module.ts文件中导入并注册新创建的服务:

    import { Module } from '@nestjs/common';
    import { UsersController } from './users.controller';
    import { UsersService } from './users.service';
    
    @Module({
      imports: [],
      controllers: [UsersController],
      providers: [UsersService],
    })
    export class UsersModule {}

服务通常通过@Injectable装饰器声明,并在模块中通过providers数组进行注册。

3.3 注解(Decorator)

注解是 Nest.js 中用于修饰类、方法或属性的重要工具,用于改变类、方法或属性的行为。Nest.js 中常用的注解包括:

  • @Controller:定义控制器
  • @Injectable:声明服务
  • @Get@Post:定义 HTTP 路由
  • @Module:定义模块

注解可以改变类或方法的行为,或增强它们的功能。例如,前面介绍的@Controller@Injectable@Get等注解用于定义控制器、服务和路由。

3.4 依赖注入(Dependency Injection)

依赖注入是一种设计模式,用于解耦组件之间的依赖关系。在 Nest.js 中,依赖注入通过容器来管理服务的创建和提供,从而简化了服务的使用和维护。

在 Nest.js 中,依赖注入通过装饰器和提供者来实现。在服务中,可以通过@Injectable装饰器声明一个提供者,然后在模块中通过providers数组进行注册。在控制器或服务中,可以通过@Inject装饰器使用已注册的提供者。

依赖注入的实现过程如下:

  1. 在服务中使用@Injectable装饰器声明一个提供者:

    import { Injectable } from '@nestjs/common';
    
    @Injectable()
    export class UsersService {
      getUsers() {
        return [
          { id: 1, name: 'John Doe' },
          { id: 2, name: 'Jane Doe' },
        ];
      }
    }
  2. 在模块中通过providers数组注册提供者:

    import { Module } from '@nestjs/common';
    import { UsersController } from './users.controller';
    import { UsersService } from './users.service';
    
    @Module({
      imports: [],
      controllers: [UsersController],
      providers: [UsersService],
    })
    export class UsersModule {}
  3. 在控制器中使用@Inject装饰器注入依赖的服务:

    import { Controller } from '@nestjs/common';
    import { UsersService } from './users.service';
    
    @Controller('users')
    export class UsersController {
      constructor(private readonly usersService: UsersService) {}
    
      getUsers() {
        return this.usersService.getUsers();
      }
    }

通过这种方式,可以实现松耦合的设计,使得代码更加灵活和易于维护。

4. 实战案例:用户管理系统

4.1 数据库连接与配置

在这个实战案例中,我们将构建一个简单的用户管理系统,实现用户数据的增删改查功能。首先,我们要连接数据库并进行配置。

Nest.js 支持多种数据库,这里我们以 MySQL 为例,用 TypeORM 作为 ORM(对象关系映射)工具。

  1. 安装数据库连接依赖:

    npm install --save @nestjs/typeorm mysql
  2. 配置数据库连接:
    在项目的根目录下创建一个 ormconfig.json 文件,配置数据库连接信息:

    {
      "type": "mysql",
      "host": "localhost",
      "port": 3306,
      "username": "root",
      "password": "password",
      "database": "nestjs",
      "synchronize": true,
      "logging": true,
      "entities": ["src/**/*.entity{.ts,.js}"]
    }
  3. 在主模块中引入 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: 'nestjs',
          synchronize: true,
          logging: true,
          entities: ['src/**/*.entity{.ts,.js}'],
        }),
        UsersModule,
      ],
      controllers: [],
      providers: [],
    })
    export class AppModule {}

4.2 创建用户模型与服务

接下来,我们将创建用户模型和对应的业务逻辑服务。

  1. 生成用户实体文件:

    nest generate entity user
  2. 编辑生成的用户实体文件,定义用户模型:

    import { Entity, Column, PrimaryGeneratedColumn } from 'typeorm';
    
    @Entity()
    export class User {
      @PrimaryGeneratedColumn()
      id: number;
    
      @Column()
      name: string;
    
      @Column()
      email: string;
    }
  3. 生成用户服务文件:

    nest generate service user
  4. 编辑生成的用户服务文件,实现用户业务逻辑:

    import { Injectable } from '@nestjs/common';
    import { InjectRepository } from '@nestjs/typeorm';
    import { Repository } from 'typeorm';
    import { User } from '../users/user.entity';
    
    @Injectable()
    export class UserService {
      constructor(
        @InjectRepository(User)
        private userRepository: Repository<User>,
      ) {}
    
      async createUser(name: string, email: string) {
        const user = this.userRepository.create({ name, email });
        return this.userRepository.save(user);
      }
    
      async getUsers() {
        return this.userRepository.find();
      }
    
      async getUserById(id: number) {
        return this.userRepository.findOne({ where: { id } });
      }
    
      async updateUser(id: number, name: string, email: string) {
        const user = await this.userRepository.findOne({ where: { id } });
        if (user) {
          user.name = name;
          user.email = email;
          return this.userRepository.save(user);
        }
        return null;
      }
    
      async deleteUser(id: number) {
        const user = await this.userRepository.findOne({ where: { id } });
        if (user) {
          return this.userRepository.remove(user);
        }
        return null;
      }
    }

4.3 实现用户增删改查功能

接下来,我们将创建控制器来处理用户管理的 HTTP 请求,调用用户服务来实现具体的业务逻辑。

  1. 生成用户控制器文件:

    nest generate controller user
  2. 编辑生成的用户控制器文件,定义路由和处理函数:

    import { Controller, Get, Post, Put, Delete, Param, Body } from '@nestjs/common';
    import { UserService } from './user.service';
    import { User } from './user.entity';
    
    @Controller('users')
    export class UserController {
      constructor(private readonly userService: UserService) {}
    
      @Get()
      async getUsers() {
        return this.userService.getUsers();
      }
    
      @Get(':id')
      async getUserById(@Param('id') id: number) {
        return this.userService.getUserById(id);
      }
    
      @Post()
      async createUser(@Body() user: User) {
        return this.userService.createUser(user.name, user.email);
      }
    
      @Put(':id')
      async updateUser(@Param('id') id: number, @Body() user: User) {
        return this.userService.updateUser(id, user.name, user.email);
      }
    
      @Delete(':id')
      async deleteUser(@Param('id') id: number) {
        return this.userService.deleteUser(id);
      }
    }
  3. users.module.ts文件中,导入并注册新创建的控制器和服务:

    import { Module } from '@nestjs/common';
    import { TypeOrmModule } from '@nestjs/typeorm';
    import { UserController } from './user.controller';
    import { UserService } from './user.service';
    import { User } from './user.entity';
    
    @Module({
      imports: [TypeOrmModule.forFeature([User])],
      controllers: [UserController],
      providers: [UserService],
    })
    export class UsersModule {}

通过上述步骤,我们已经成功构建了一个简单的用户管理系统,可以实现用户数据的增删改查功能。

5. 测试Nest.js应用

5.1 单元测试与集成测试

单元测试和集成测试是软件测试的两种常见类型。单元测试用于测试单个函数或组件,而集成测试则用于测试组件之间的交互。

5.2 使用Jest进行测试

Jest 是一个流行的 JavaScript 测试框架,可以与 Nest.js 无缝集成,用于执行单元测试和集成测试。

  1. 安装 Jest 和相关依赖:

    npm install --save-dev jest ts-jest @types/jest
  2. 配置 Jest:
    在项目的根目录下创建一个 jest.config.js 文件,配置 Jest:

    module.exports = {
      preset: 'ts-jest',
      testRegex: 'src/(.*)\\.(test|spec)\\.ts$',
      moduleFileExtensions: ['ts', 'js'],
      transform: {
        '^.+\\.ts$': 'ts-jest',
      },
    };
  3. 在控制器或服务中编写测试用例:
    例如,在 user.controller.spec.ts 文件中,我们可以编写如下测试用例:

    import { Test, TestingModule } from '@nestjs/testing';
    import { UserController } from './user.controller';
    import { UserService } from '../user.service';
    import { User } from '../user.entity';
    import { NotFoundException, BadRequestException } from '@nestjs/common';
    
    const mockUserService = () => ({
      getUsers: jest.fn(() => Promise.resolve([])),
      getUserById: jest.fn().mockResolvedValue({}),
      createUser: jest.fn().mockResolvedValue({}),
      updateUser: jest.fn().mockResolvedValue({}),
      deleteUser: jest.fn().mockResolvedValue({}),
    });
    
    describe('UserController', () => {
      let userController: UserController;
      let userService: UserService;
    
      beforeEach(async () => {
        const module: TestingModule = await Test.createTestingModule({
          controllers: [UserController],
          providers: [{ provide: UserService, useFactory: mockUserService }],
        }).compile();
    
        userController = module.get<UserController>(UserController);
        userService = module.get<UserService>(UserService);
      });
    
      it('should return all users', async () => {
        const users = await userController.getUsers();
        expect(users).toEqual([]);
      });
    
      it('should return a user by id', async () => {
        const user = await userController.getUserById(1);
        expect(user).toEqual({});
      });
    
      it('should create a user', async () => {
        const user = await userController.createUser({ name: 'John Doe', email: 'john@example.com' });
        expect(user).toEqual({});
      });
    
      it('should update a user', async () => {
        const user = await userController.updateUser(1, { name: 'Jane Doe', email: 'jane@example.com' });
        expect(user).toEqual({});
      });
    
      it('should delete a user', async () => {
        const user = await userController.deleteUser(1);
        expect(user).toEqual({});
      });
    
      it('should return an error if user not found', async () => {
        try {
          await userController.getUserById(999);
        } catch (error) {
          expect(error).toBeInstanceOf(NotFoundException);
        }
      });
    
      it('should return an error if user update fails', async () => {
        try {
          await userController.updateUser(999, { name: 'Test', email: 'test@example.com' });
        } catch (error) {
          expect(error).toBeInstanceOf(BadRequestException);
        }
      });
    });

在服务文件中编写测试用例,例如在 user.service.spec.ts 文件中:

import { Test, TestingModule } from '@nestjs/testing';
import { UserService } from './user.service';
import { User } from './user.entity';
import { NotFoundException, BadRequestException } from '@nestjs/common';

const mockUserRepository = () => ({
  create: jest.fn(),
  save: jest.fn().mockResolvedValue({}),
  find: jest.fn(() => Promise.resolve([])),
  findOne: jest.fn().mockResolvedValue({}),
  remove: jest.fn().mockResolvedValue({}),
});

describe('UserService', () => {
  let userService: UserService;

  beforeEach(async () => {
    const module: TestingModule = await Test.createTestingModule({
      providers: [
        {
          provide: UserService,
          useFactory: (userRepository) => new UserService(userRepository),
          inject: [UserRepository],
        },
        {
          provide: UserRepository,
          useFactory: mockUserRepository,
        },
      ],
    }).compile();

    userService = module.get<UserService>(UserService);
  });

  it('should return all users', async () => {
    const users = await userService.getUsers();
    expect(users).toEqual([]);
  });

  it('should return a user by id', async () => {
    const user = await userService.getUserById(1);
    expect(user).toEqual({});
  });

  it('should create a user', async () => {
    const user = await userService.createUser('John Doe', 'john@example.com');
    expect(user).toEqual({});
  });

  it('should update a user', async () => {
    const user = await userService.updateUser(1, 'Jane Doe', 'jane@example.com');
    expect(user).toEqual({});
  });

  it('should delete a user', async () => {
    const user = await userService.deleteUser(1);
    expect(user).toEqual({});
  });

  it('should throw an error if user not found', async () => {
    try {
      await userService.getUserById(999);
    } catch (error) {
      expect(error).toBeInstanceOf(NotFoundException);
    }
  });

  it('should throw an error if user update fails', async () => {
    try {
      await userService.updateUser(999, 'Test', 'test@example.com');
    } catch (error) {
      expect(error).toBeInstanceOf(BadRequestException);
    }
  });
});

通过这种方式,我们可以更好地控制测试环境,验证控制器方法的行为。

6. 部署与上线

6.1 构建项目

在项目开发完成后,需要构建项目以便部署到生产环境。

  1. 构建项目:

    npm run build

    这将生成一个 dist 文件夹,其中包含编译后的代码。

  2. 检查编译后的代码:
    ls dist

6.2 部署到服务器

部署到服务器的步骤取决于你的服务器环境。这里以主流的Linux服务器为例,提供一个简单的部署流程。

  1. 上传编译后的代码到服务器。可以使用SCP、FTP等工具上传到服务器指定的目录中。

    scp -r dist user@server:/path/to/deploy
  2. 安装运行时依赖。确保安装了 Node.js 和 npm。

    cd /path/to/deploy
    npm install --production
  3. 启动应用:
    npm run start:prod

    或者使用 PM2 或其他进程管理工具来管理应用进程。

6.3 配置服务器环境

为了确保应用在生产环境中稳定运行,需要对服务器环境进行配置。

  1. 设置环境变量
    创建 .env 文件,定义环境变量。

    NODE_ENV=production
    PORT=3000
  2. 配置Nginx
    创建一个 Nginx 配置文件,将所有 HTTP 请求代理到 Node.js 应用。

    server {
     listen 80;
     server_name example.com;
    
     location / {
       proxy_pass http://localhost:3000;
       proxy_http_version 1.1;
       proxy_set_header Upgrade $http_upgrade;
       proxy_set_header Connection 'upgrade';
       proxy_set_header Host $host;
       proxy_cache_bypass $http_upgrade;
     }
    }
  3. 配置PM2
    使用 PM2 管理应用进程。安装 PM2:
    npm install pm2 -g

    设置应用:

    pm2 start dist/main.js --name "nestjs-app"
    pm2 startup
    pm2 save
    pm2 startup

通过以上步骤,我们可以顺利地将 Nest.js 项目部署到生产环境,并确保其稳定运行。

点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消