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框架的第一步是确保你已经安装了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 CLI。
- 使用nest new命令创建一个新的Nest项目。
- 启动项目并访问默认的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接口通常涉及以下几个步骤:
- 创建控制器。
- 创建服务。
- 在控制器中使用服务。
创建控制器
控制器负责处理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。
提供者是Nest框架中的服务或控制器。提供者使用@Injectable装饰器标记,并通过@Module装饰器的providers数组来注册。提供者可以依赖注入其他提供者,从而实现模块化和解耦的代码结构。
示例代码
定义一个简单的提供者:
import { Injectable } from '@nestjs/common';
@Injectable()
export class UserService {
  getUsers(): string {
    return 'All users';
  }
}控制器是Nest框架中处理HTTP请求的核心组件。每个控制器都包含一个或多个装饰器,用于定义HTTP方法(如@Get)和路由路径。
示例代码
创建一个简单的控制器来处理HTTP GET请求:
import { Controller, Get } from '@nestjs/common';
@Controller('users')
export class UserController {
  @Get()
  findAll(): string {
    return 'All users';
  }
}服务是业务逻辑的实现载体。在Nest框架中,服务通常使用@Injectable装饰器来标记。服务的作用是封装和组织应用的业务逻辑,使其更易于测试和维护。
示例代码
创建一个简单的服务来返回所有用户:
import { Injectable } from '@nestjs/common';
@Injectable()
export class UserService {
  getUsers(): string {
    return 'All users';
  }
}模块是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接口来查询和操作用户信息。
安装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进行环境隔离的示例。
创建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.yml文件,用于管理Docker容器。
version: '3'
services:
  app:
    build: .
    ports:
      - "3000:3000"
    environment:
      - PORT=3000
      - DATABASE_URL=mongodb://localhost:27017/mydatabase使用以下命令启动Docker容器:
docker-compose upNest框架提供了强大的错误处理功能,可以通过全局异常过滤器来捕获和处理异常。
全局异常过滤器示例
创建一个全局异常过滤器,用于捕获和处理异常。
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应用部署到云环境通常涉及以下几个步骤:
- 使用Docker容器化应用。
- 使用Docker Compose或Kubernetes管理应用。
- 使用云提供商的平台和服务部署应用。
使用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.yaml和service.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的官方文档。
共同学习,写下你的评论
评论加载中...
作者其他优质文章
 
                 
             
			 
					 
					