手记

Nest学习:新手入门全面指南

概述

Nest框架是一个基于Angular和Node.js的高效、模块化和可扩展的JavaScript框架,利用了TypeScript语言的强类型特性。它通过装饰器和依赖注入简化了复杂的业务逻辑实现,nest学习者可以通过本文了解其特点、安装方法和基本使用。文章还详细介绍了如何创建和测试简单的API接口,以及如何使用GraphQL增强API能力。

Nest框架简介
什么是Nest框架

Nest框架是一个基于Angular和Node.js的高效、模块化和可扩展的JavaScript框架。它利用了TypeScript语言的强类型特性,为开发者提供了更好的代码结构和类型检查。Nest框架的设计理念是遵循面向对象编程的原则,通过使用装饰器和依赖注入来简化复杂的业务逻辑实现。

Nest框架的特点和优势
  • 可扩展性:Nest框架的模块化设计使得你可以轻松地添加新的功能模块,而无需对现有代码进行大规模修改。
  • 可维护性:清晰的模块划分和细致的代码结构使得代码易于理解和维护。
  • 高效性:通过装饰器和依赖注入,Nest框架能够提供高效且简洁的代码实现。
  • 可测试性:Nest框架支持单元测试和集成测试,使得软件的测试变得更加容易。
  • 类型安全:利用TypeScript的强类型特性,Nest框架能够提供更好的类型检查,减少运行时错误。
安装Nest框架

安装Nest框架的第一步是确保你已经安装了Node.js和npm。接下来,使用npm全局安装Nest CLI,这是一个命令行工具,用于生成项目和管理项目文件。

npm install -g @nestjs/cli

安装完成后,可以使用nest new命令来创建一个新的Nest项目:

nest new my-nest-app

上述命令会生成一个基础的Nest项目结构。你可以使用如下命令来启动项目:

npm run start
快速上手Nest
创建第一个Nest项目

创建一个Nest项目的基本步骤如下:

  1. 安装Nest CLI。
  2. 使用nest new命令创建一个新的Nest项目。
  3. 启动项目并访问默认的API端点。

如上所述,安装Nest CLI后,可以使用以下命令创建一个新的Nest项目:

nest new my-nest-app

创建项目后,进入项目目录并启动项目:

cd my-nest-app
npm run start

启动项目后,访问http://localhost:3000/api,可以看到默认的欢迎信息。

项目结构解析

一个Nest项目通常包含以下主要文件和目录:

  • src/:项目的源代码目录,包含模块、服务、控制器等。
  • app.module.ts:应用的主模块文件,定义了应用的基本结构。
  • main.ts:应用的启动文件,配置了应用的入口点。
  • nest-cli.json:Nest CLI配置文件。
  • package.json:项目的npm配置文件。
  • README.md:项目的说明文档。

app.module.ts 示例代码

app.module.ts是项目的主模块文件,定义了应用的基本结构:

import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';

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

main.ts 示例代码

main.ts是应用的启动文件,配置了应用的入口点:

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

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  await app.listen(3000);
}
bootstrap();
实现简单的API接口

创建一个简单的API接口通常涉及以下几个步骤:

  1. 创建控制器。
  2. 创建服务。
  3. 在控制器中使用服务。

创建控制器

控制器负责处理HTTP请求。创建一个简单的控制器:

nest generate controller user

生成的user.controller.ts文件如下:

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

@Controller('users')
export class UserController {
  @Get()
  findAll(): string {
    return 'All users';
  }
}

创建服务

服务是业务逻辑实现的载体。创建一个简单的服务:

nest generate service user

生成的user.service.ts文件如下:

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

@Injectable()
export class UserService {
  getUsers(): string {
    return 'All users';
  }
}

在控制器中使用服务

修改控制器文件,使用服务来处理HTTP请求:

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

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

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

现在启动项目并访问http://localhost:3000/users,可以看到返回的结果为All users

提供者(Providers)

提供者是Nest框架中的服务或控制器。提供者使用@Injectable装饰器标记,并通过@Module装饰器的providers数组来注册。提供者可以依赖注入其他提供者,从而实现模块化和解耦的代码结构。

示例代码

定义一个简单的提供者:

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

@Injectable()
export class UserService {
  getUsers(): string {
    return 'All users';
  }
}
基础概念详解
控制器(Controllers)

控制器是Nest框架中处理HTTP请求的核心组件。每个控制器都包含一个或多个装饰器,用于定义HTTP方法(如@Get)和路由路径。

示例代码

创建一个简单的控制器来处理HTTP GET请求:

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

@Controller('users')
export class UserController {
  @Get()
  findAll(): string {
    return 'All users';
  }
}
服务(Services)

服务是业务逻辑的实现载体。在Nest框架中,服务通常使用@Injectable装饰器来标记。服务的作用是封装和组织应用的业务逻辑,使其更易于测试和维护。

示例代码

创建一个简单的服务来返回所有用户:

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

@Injectable()
export class UserService {
  getUsers(): string {
    return 'All users';
  }
}
模块(Modules)

模块是Nest框架中的核心概念之一。一个模块定义了一组控制器、服务和其他模块之间的依赖关系。模块通过@Module装饰器来定义。

示例代码

定义一个简单的模块,包含一个控制器和一个服务:

import { Module } from '@nestjs/common';
import { UserController } from './user.controller';
import { UserService } from './user.service';

@Module({
  controllers: [UserController],
  providers: [UserService],
})
export class UserModule {}
实践案例:构建用户管理系统
用户注册和登录功能

在本章节中,我们将实现一个简单的用户注册和登录功能。首先,我们将创建一个注册接口,接收用户信息并将其存储在内存中。然后,我们将实现一个登录接口,验证用户信息并返回一个token。

创建用户模块

首先,我们需要创建一个用户模块来管理用户相关的服务和控制器。

nest generate module user

生成的user.module.ts文件如下:

import { Module } from '@nestjs/common';
import { UserService } from './user.service';
import { UserController } from './user.controller';

@Module({
  providers: [UserService],
  controllers: [UserController],
})
export class UserModule {}

创建用户服务

接下来,我们需要创建一个用户服务,用于处理用户注册和登录逻辑。

nest generate service user

生成的user.service.ts文件如下:

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

@Injectable()
export class UserService {
  private users = new Map<string, { username: string; password: string }>();

  register(username: string, password: string): boolean {
    if (this.users.has(username)) {
      return false;
    }
    this.users.set(username, { username, password });
    return true;
  }

  login(username: string, password: string): string | null {
    const user = this.users.get(username);
    if (!user) {
      return null;
    }
    if (user.password === password) {
      return 'token';
    }
    return null;
  }
}

创建用户控制器

现在,我们需要创建一个用户控制器来处理HTTP请求。

nest generate controller user

生成的user.controller.ts文件如下:

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

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

  @Post('register')
  register(@Body() body: { username: string; password: string }): string {
    const { username, password } = body;
    if (this.userService.register(username, password)) {
      return 'User registered successfully';
    }
    return 'Username already exists';
  }

  @Post('login')
  login(@Body() body: { username: string; password: string }): string | null {
    return this.userService.login(body.username, body.password);
  }
}

测试注册和登录接口

启动项目并使用Postman测试注册和登录接口。

  • 注册接口:http://localhost:3000/users/register
  • 登录接口:http://localhost:3000/users/login
用户信息管理

接下来,我们将实现一个简单的用户信息管理功能,包括获取用户列表和删除用户。

更新用户服务

首先,我们需要更新用户服务,添加获取用户列表和删除用户的方法。

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

@Injectable()
export class UserService {
  private users = new Map<string, { username: string; password: string }>();

  register(username: string, password: string): boolean {
    if (this.users.has(username)) {
      return false;
    }
    this.users.set(username, { username, password });
    return true;
  }

  login(username: string, password: string): string | null {
    const user = this.users.get(username);
    if (!user) {
      return null;
    }
    if (user.password === password) {
      return 'token';
    }
    return null;
  }

  getUsers(): { username: string }[] {
    return Array.from(this.users.keys()).map((username) => ({ username }));
  }

  deleteUser(username: string): boolean {
    if (!this.users.has(username)) {
      return false;
    }
    this.users.delete(username);
    return true;
  }
}

更新用户控制器

接下来,我们需要更新用户控制器,添加获取用户列表和删除用户的方法。

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

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

  @Post('register')
  register(@Body() body: { username: string; password: string }): string {
    const { username, password } = body;
    if (this.userService.register(username, password)) {
      return 'User registered successfully';
    }
    return 'Username already exists';
  }

  @Post('login')
  login(@Body() body: { username: string; password: string }): string | null {
    return this.userService.login(body.username, body.password);
  }

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

  @Delete(':username')
  deleteUser(@Param('username') username: string): string {
    if (this.userService.deleteUser(username)) {
      return 'User deleted successfully';
    }
    return 'User not found';
  }
}

测试用户信息管理接口

启动项目并使用Postman测试获取用户列表和删除用户接口。

  • 获取用户列表接口:http://localhost:3000/users
  • 删除用户接口:http://localhost:3000/users/{username}
使用GraphQL增强API能力

接下来,我们将使用GraphQL增强API能力,实现一个简单的GraphQL接口来查询和操作用户信息。

安装Apollo库

首先,我们需要安装Apollo库来支持GraphQL。

npm install @nestjs/graphql @nestjs/apollo graphql

更新用户服务

接下来,我们需要更新用户服务,添加GraphQL查询和操作的方法。

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

@Injectable()
export class UserService {
  private users = new Map<string, { username: string; password: string }>();

  register(username: string, password: string): boolean {
    if (this.users.has(username)) {
      return false;
    }
    this.users.set(username, { username, password });
    return true;
  }

  login(username: string, password: string): string | null {
    const user = this.users.get(username);
    if (!user) {
      return null;
    }
    if (user.password === password) {
      return 'token';
    }
    return null;
  }

  getUsers(): { username: string }[] {
    return Array.from(this.users.keys()).map((username) => ({ username }));
  }

  deleteUser(username: string): boolean {
    if (!this.users.has(username)) {
      return false;
    }
    this.users.delete(username);
    return true;
  }

  getUser(username: string): { username: string } | null {
    if (this.users.has(username)) {
      return { username };
    }
    return null;
  }
}

创建GraphQL模块

接下来,我们需要创建一个GraphQL模块来定义GraphQL类型和解析器。

nest generate module graphql

生成的graphql.module.ts文件如下:

import { Module } from '@nestjs/common';
import { GraphQLModule } from '@nestjs/graphql';
import { UserResolver } from './user.resolver';
import { UserService } from '../user.service';

@Module({
  imports: [
    GraphQLModule.forRoot({
      autoSchemaFile: true,
    }),
  ],
  providers: [UserResolver],
})
export class GraphQLModule {}

创建GraphQL解析器

接下来,我们需要创建一个GraphQL解析器来实现GraphQL查询和操作。

nest generate resolver user

生成的user.resolver.ts文件如下:

import { Resolver, Query, Args } from '@nestjs/graphql';
import { UserService } from '../user.service';

@Resolver()
export class UserResolver {
  constructor(private readonly userService: UserService) {}

  @Query(() => String)
  register(@Args('username') username: string, @Args('password') password: string): string {
    if (this.userService.register(username, password)) {
      return 'User registered successfully';
    }
    return 'Username already exists';
  }

  @Query(() => String)
  login(@Args('username') username: string, @Args('password') password: string): string | null {
    return this.userService.login(username, password);
  }

  @Query(() => [String])
  getUsers(): { username: string }[] {
    return this.userService.getUsers();
  }

  @Query(() => String)
  getUser(@Args('username') username: string): { username: string } | null {
    return this.userService.getUser(username);
  }

  @Mutation(() => String)
  deleteUser(@Args('username') username: string): string {
    if (this.userService.deleteUser(username)) {
      return 'User deleted successfully';
    }
    return 'User not found';
  }
}

测试GraphQL接口

启动项目并使用GraphQL Playground测试GraphQL接口。

  • 注册用户:mutation { register(username: "test", password: "test") }
  • 登录用户:mutation { login(username: "test", password: "test") }
  • 获取用户列表:query { getUsers }
  • 获取用户信息:query { getUser(username: "test") }
  • 删除用户:mutation { deleteUser(username: "test") }
测试与调试
单元测试与集成测试

Nest框架通过整合Jest和Supertest库提供了强大的单元测试和集成测试支持。下面是一个简单的测试示例,用于验证用户注册和登录功能是否正常工作。

单元测试示例

创建一个单元测试文件,用于测试用户服务中的注册和登录逻辑。

import { Test, TestingModule } from '@nestjs/testing';
import { UserService } from './user.service';

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

  beforeEach(async () => {
    const module: TestingModule = await Test.createTestingModule({
      providers: [UserService],
    }).compile();

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

  it('should register a user', () => {
    const result = service.register('test', 'password');
    expect(result).toBe(true);
  });

  it('should not register a user twice', () => {
    service.register('test', 'password');
    const result = service.register('test', 'password');
    expect(result).toBe(false);
  });

  it('should login a user', () => {
    service.register('test', 'password');
    const token = service.login('test', 'password');
    expect(token).toBe('token');
  });

  it('should not login a user with wrong password', () => {
    service.register('test', 'password');
    const token = service.login('test', 'wrongPassword');
    expect(token).toBeNull();
  });
});

集成测试示例

创建一个集成测试文件,用于测试用户控制器中的注册和登录端点。

import { Test, TestingModule } from '@nestjs/testing';
import { INestApplication } from '@nestjs/common';
import { UserController } from './user.controller';
import { UserService } from './user.service';
import { NestExpressApplication } from '@nestjs/platform-express';
import * as request from 'supertest';

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

  beforeEach(async () => {
    const module: TestingModule = await Test.createTestingModule({
      controllers: [UserController],
      providers: [UserService],
    })
      .overrideProvider(UserService)
      .useClass(MockUserService)
      .compile();

    app = module.createNestApplication<NestExpressApplication>();
    userService = module.get<UserService>(UserService);
    await app.init();
  });

  afterAll(async () => {
    await app.close();
  });

  it('should register a user', () => {
    return request(app.getHttpServer())
      .post('/users/register')
      .send({ username: 'test', password: 'password' })
      .expect(200)
      .expect('User registered successfully');
  });

  it('should not register a user twice', () => {
    return request(app.getHttpServer())
      .post('/users/register')
      .send({ username: 'test', password: 'password' })
      .expect(200)
      .expect('User registered successfully')
      .then(() => {
        return request(app.getHttpServer())
          .post('/users/register')
          .send({ username: 'test', password: 'password' })
          .expect(200)
          .expect('Username already exists');
      });
  });

  it('should login a user', () => {
    return request(app.getHttpServer())
      .post('/users/login')
      .send({ username: 'test', password: 'password' })
      .expect(200)
      .expect('token');
  });

  it('should not login a user with wrong password', () => {
    return request(app.getHttpServer())
      .post('/users/login')
      .send({ username: 'test', password: 'wrongPassword' })
      .expect(401);
  });
});

class MockUserService {
  register(username: string, password: string): boolean {
    return true;
  }

  login(username: string, password: string): string | null {
    if (username === 'test' && password === 'password') {
      return 'token';
    }
    return null;
  }
}
使用Docker进行环境隔离

Docker是一个开源的容器化平台,可以用来创建和运行隔离的环境。下面是如何使用Docker进行环境隔离的示例。

创建Dockerfile

在项目的根目录下创建一个Dockerfile,用于构建Docker镜像。

cat > Dockerfile <<EOF
FROM node:14

WORKDIR /app

COPY package.json package-lock.json ./

RUN npm install

COPY . .

EXPOSE 3000

CMD ["npm", "run", "start"]
EOF

构建Docker镜像

使用以下命令构建Docker镜像:

docker build -t my-nest-app .

运行Docker容器

使用以下命令运行Docker容器:

docker run -p 3000:3000 my-nest-app
使用Docker Compose部署到云环境

创建一个docker-compose.yml文件,用于管理Docker容器。

version: '3'
services:
  app:
    build: .
    ports:
      - "3000:3000"
    environment:
      - PORT=3000
      - DATABASE_URL=mongodb://localhost:27017/mydatabase

使用以下命令启动Docker容器:

docker-compose up
错误处理与调试技巧

Nest框架提供了强大的错误处理功能,可以通过全局异常过滤器来捕获和处理异常。

全局异常过滤器示例

创建一个全局异常过滤器,用于捕获和处理异常。

import { ExceptionFilter, Catch, ArgumentsHost, HttpException, HttpStatus } from '@nestjs/common';

@Catch()
export class GlobalExceptionFilter implements ExceptionFilter {
  catch(exception: any, host: ArgumentsHost) {
    const ctx = host.switchToHttp();
    const response = ctx.getResponse();
    const request = ctx.getRequest();
    const status = exception instanceof HttpException ? exception.getStatus() : HttpStatus.INTERNAL_SERVER_ERROR;

    response.status = status;
    response.json({
      statusCode: status,
      timestamp: new Date().toISOString(),
      path: request.url,
      message: exception.message,
    });
  }
}

注册全局异常过滤器

app.module.ts文件中注册全局异常过滤器。

import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { GlobalExceptionFilter } from './global.filter';

@Module({
  imports: [],
  controllers: [AppController],
  providers: [AppService, { provide: APP_FILTER, useClass: GlobalExceptionFilter }],
})
export class AppModule {}
部署与维护
构建项目和环境变量管理

在部署Nest应用之前,你需要构建项目并管理环境变量。

构建项目

使用以下命令构建项目:

npm run build

管理环境变量

创建一个.env文件来管理环境变量。

cat > .env <<EOF
PORT=3000
DATABASE_URL=mongodb://localhost:27017/mydatabase
EOF

使用环境变量

main.ts文件中使用环境变量。

import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import * as dotenv from 'dotenv';

dotenv.config();

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  await app.listen(process.env.PORT);
}
bootstrap();
部署到云环境

将Nest应用部署到云环境通常涉及以下几个步骤:

  1. 使用Docker容器化应用。
  2. 使用Docker Compose或Kubernetes管理应用。
  3. 使用云提供商的平台和服务部署应用。

使用Docker部署到云环境

使用Docker容器化应用,并使用Docker Compose或Kubernetes管理应用。

使用Docker Compose部署到云环境

创建一个docker-compose.yml文件,用于管理Docker容器。

version: '3'
services:
  app:
    build: .
    ports:
      - "3000:3000"
    environment:
      - PORT=3000
      - DATABASE_URL=mongodb://localhost:27017/mydatabase

使用以下命令启动Docker容器:

docker-compose up

使用Kubernetes部署到云环境

创建一个deployment.yamlservice.yaml文件,用于管理Kubernetes资源。

# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-nest-app
spec:
  replicas: 1
  selector:
    matchLabels:
      app: my-nest-app
  template:
    metadata:
      labels:
        app: my-nest-app
    spec:
      containers:
      - name: my-nest-app
        image: my-nest-app
        ports:
        - containerPort: 3000
        env:
        - name: PORT
          value: "3000"
        - name: DATABASE_URL
          value: "mongodb://localhost:27017/mydatabase"
# service.yaml
apiVersion: v1
kind: Service
metadata:
  name: my-nest-app
spec:
  selector:
    app: my-nest-app
  ports:
  - protocol: TCP
    port: 80
    targetPort: 3000

使用以下命令部署应用:

kubectl apply -f deployment.yaml
kubectl apply -f service.yaml
日志管理和监控

日志管理和监控是应用部署后的重要环节,可以使用多种工具来实现。下面是如何使用Prometheus和Grafana进行监控和日志管理的示例。

使用Prometheus和Grafana进行监控

创建一个prometheus.yml文件,用于配置Prometheus的监控。

global:
  scrape_interval: 15s

scrape_configs:
  - job_name: 'nest-app'
    static_configs:
      - targets: ['localhost:3000']

创建一个grafana.json文件,用于配置Grafana的监控。

{
  "dashboard": {
    "id": -1,
    "title": "Nest App",
    "panels": [
      {
        "type": "graph",
        "title": "Requests per second",
        "yAxes": [
          {
            "label": "Requests/second",
            "format": "short"
          }
        ],
        "lines": true,
        "fill": 1,
        "nullPointMode": "connected",
        "xaxis": {
          "mode": "time"
        },
        "yaxis": {
          "align": false,
          "alignLevel": null
        },
        "targets": [
          {
            "expr": "rate(nest_app_requests_total[5m])",
            "interval": "",
            "legendFormat": "Requests",
            "refId": "A"
          }
        ]
      }
    ]
  }
}

使用Prometheus和Grafana进行监控的具体步骤可以参考Prometheus和Grafana的官方文档。

0人推荐
随时随地看视频
慕课网APP