废话不多说!

直接忽略掉没有用的理论!

实战!走起!

10分钟上手nest.js+mongoDB_ide

1.安装nest.js脚手架

cnpm i -g @nestjs/cli

2.创建nest项目

nest new 项目名

3.安装mongoose

项目中我们会用到 Mongoose 来操作我们的数据库,Nest 官方为我们提供了一个 Mongoose 的封装,我们需要安装 mongoose@nestjs/mongoose

npm install mongoose @nestjs/mongoose --save

4.创建模块

nest g module user server

脚手架工具会自动在 src/server/user 文件夹下创建一个 user.module.ts,这是 Nest 的模块文件,Nest 用它来组织整个应用程序的结构。

// user.module.ts
import { Module } from '@nestjs/common';
@Module({})
export class UserModule {}

同时还会在根模块 app.module.ts 中引入 UserModule 这个模块,相当于一个树形结构,在根模块中引入了 User 模块。

执行上面的终端命令之后,app.module.ts 中的代码已经发生了变化,在文件顶部自动引入了 UserModule,同时也在 @Module 装饰器的 imports 中引入了 UserModule

// app.module.ts
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { UserModule } from './server/user/user.module'; // 自动引入
@Module({
  imports: [UserModule], // 自动引入
  controllers: [AppController],
  providers: [AppService]
})
export class AppModule {}

5.创建控制器

nest g controller user server

Nest 中,controller 就类似前端的路由,负责处理客户端传入的请求服务端返回的响应

举个例子,我们如果要通过 http://localhost:3000/user/users 获取所有的用户信息,那么我们可以在 UserController 中创建一个 GET 方法,路径为 users 的路由,这个路由负责返回所有的用户信息。

// user.controller.ts
import { Controller, Get } from '@nestjs/common';
@Controller('user')
export class UserController {
  @Get('users')
  findAll(): string {
    return "All User's Info"; // [All User's Info] 暂时代替所有用户的信息
  }
}

这就是 controller 的作用,负责分发和处理请求响应

当然,也可以把 findAll 方法写成异步方法,像这样:

// user.controller.ts
import { Controller, Get } from '@nestjs/common';
@Controller('user')
export class UserController {
  @Get('users')
  async findAll(): Promise<any> {
    return await this.xxx.xxx(); // 一些异步操作
  }
}

6.创建 Provider

nest g service user server

provider 我们可以简单地从字面意思来理解,就是服务的提供者

怎么去理解这个服务提供者呢?

举个例子,我们的 controller 接收到了一个用户的查询请求,我们不能直接在 controller 中去查询数据库并返回,而是要将查询请求交给 provider 来处理,这里我们创建了一个 UserService,就是用来提供数据库操作服务的。

// user.service.ts
import { Injectable } from '@nestjs/common';
@Injectable()
export class UserService {}

当然,provider 不一定只能用来提供数据库的操作服务,还可以用来做一些用户校验,比如使用 JWT 对用户权限进行校验的策略,就可以写成一个策略类,放到 provider 中,为模块提供相应的服务。

controllerprovider 都创建完后,user.module.ts 文件中多了一些代码,变成了这样:

// user.module.ts
import { Module } from '@nestjs/common';
import { UserController } from './user.controller';
import { UserService } from './user.service';
@Module({
  controllers: [UserController],
  providers: [UserService]
})
export class UserModule {}

7.连接数据库引入 根模块Mongoose

连接数据之前,我们要先在根模块,也就是 app.module.ts 中引入 Mongoose 的连接模块:

// app.module.ts
import { Module } from '@nestjs/common';
import { MongooseModule } from '@nestjs/mongoose';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { UserModule } from './server/user/user.module';
@Module({  imports: [MongooseModule.forRoot('mongodb://localhost:27017/Users'), UserModule],
  controllers: [AppController],
  providers: [AppService]
})
export class AppModule {}

如果这里运行后端文件是又可能会报错,如果报错就添加:

npm install @types/mongoose --dev

安装完之后服务就正常重启了。

8.引入 分模块Mongoose

这里我们先要创建一个数据表的格式,在 src/server/user 文件夹下创建一个 user.schema.ts 文件,定义一个数据表的格式:

// user.schema.ts
import { Schema } from 'mongoose';
export const userSchema = new Schema({
  _id: { type: String, required: true }, // 覆盖 Mongoose 生成的默认 _id
  user_name: { type: String, required: true },
  password: { type: String, required: true }
});

然后将我们的 user.module.ts 文件修改成这样:

// user.module.ts
import { Module } from '@nestjs/common';
import { MongooseModule } from '@nestjs/mongoose';
import { UserController } from './user.controller';
import { userSchema } from './user.schema';
import { UserService } from './user.service';
@Module({
  imports: [MongooseModule.forFeature([{ name: 'Users', schema: userSchema }])],
  controllers: [UserController],
  providers: [UserService]
})
export class UserModule {}

9.CRUD

创建两个文件做数据类型的定义:

user.interface.ts

// user.interface.ts
import { Document } from 'mongoose';
export interface User extends Document {
//   readonly _id: string;
  readonly user_name: string;
  readonly password: string;
}

user.dto.ts

// user.dto.ts
export class CreateUserDTO {
    readonly user_name: string;
    readonly password: string;
  }  
  export class EditUserDTO {
    readonly user_name: string;
    readonly password: string;
  }

我们打开 user.service.ts 文件,为 UserService 类添加一个构造函数,让其在实例化的时候能够接收到数据库 Model,这样才能在类中的方法里操作数据库。

import { Injectable } from '@nestjs/common';
import { InjectModel } from '@nestjs/mongoose';
import { Model } from 'mongoose';
import { CreateUserDTO, EditUserDTO } from './user.dto';
import { User } from './user.interface';
@Injectable()
export class UserService {
    constructor(@InjectModel('Users') private readonly userModel: Model<User>) { }
    // 查找所有用户
    async findAll(): Promise<User[]> {
        const users = await this.userModel.find();
        return users;
    }
    // 查找单个用户
    async findOne(user_name: string): Promise<User> {
        return await this.userModel.findOne({user_name});
    }
    // 添加单个用户
    async addOne(body: CreateUserDTO): Promise<void> {
        await this.userModel.create(body);
    }
    // 编辑单个用户
    async updateOne(user_name: string, newPassword: string): Promise<void> {
        await this.userModel.updateOne({user_name},{password: newPassword});
    }
    // 删除单个用户
    async deleteOne(user_name: string): Promise<void> {
        await this.userModel.deleteOne({user_name});
    }
}

因为 mongoose 操作数据库其实是异步的,所以这里我们使用 async 函数来处理异步的过程。

现在,我们可以到 user.controller.ts 中设置路由了,将客户端的请求进行处理,调用相应的服务实现相应的功能:

import {
    Controller,
    Body,
    // Delete,
    Get,
    Param,
    Post,
    Put
} from '@nestjs/common';
import { CreateUserDTO, EditUserDTO } from './user.dto';
import { User } from './user.interface';
import { UserService } from './user.service';
interface UserResponse<T = unknown> {//这是一个 TypeScript 接口定义,用于描述用户响应的数据结构。它包含一个泛型参数 T,默认值为 unknown,其中包含 code(响应码)、data(响应数据,可选)和 message(响应消息)三个属性。
    code: number;
    data?: T;
    message: string;
}
@Controller('user')
export class UserController {
    constructor(private readonly userService: UserService) { }
    // GET /user/users
    @Get('/users')
    async findAll(): Promise<UserResponse<User[]>> {
        return {
            code: 200,
            data: await this.userService.findAll(),
            message: '查询成功.'
        };
    }
    // GET /user/:_id
    @Post('/find_one')
    async findOne(@Body() userData: { user_name: string }): Promise<UserResponse> {
        await this.userService.findOne(userData.user_name); // 使用传入的 user_name 参数
        return {
            code: 200,
            data: await this.userService.findOne(userData.user_name),
            message: '查询成功.'
        };
    }
    // POST /user/user
    @Post('/user')
    async addOne(@Body() body: CreateUserDTO): Promise<UserResponse> {
        await this.userService.addOne(body);
        return {
            code: 200,
            message: '添加成功.'
        };
    }
    // PUT /user/:_id
    @Post('/upd')
    async updateOne(@Body() userData: { user_name: string, newPassword: string }): Promise<UserResponse> {
        await this.userService.updateOne(userData.user_name, userData.newPassword); // 使用传入的 user_name 参数
        return {
            code: 200,
            message: '修改成功.'
        };
    }
    // Post /user/deluser
    @Post('/deluser1')
    async deleteOne(@Body() userData: { user_name: string }): Promise<UserResponse> {
        await this.userService.deleteOne(userData.user_name); // 使用传入的 user_name 参数
        return {
            code: 200,
            message: '删除成功.'
        };
    }
}

好的,到这里我们的nest.js一个调用mongoose来操作mongoDB数据库的后端程序就已经写好了。

我们接下来用小程序端作为前端来调用后端,将整个完整的前后端+数据库程序跑起来!

10.前端部分

index.js

// pages/index1/index1.js
Page({

  /**
   * 页面的初始数据
   */
  data: {
  },
  // 增加

  // 输入框1的输入事件(姓名)
  input1Change(e) {
    this.setData({
      inputValue1: e.detail.value,
    });
  },

  // 输入框2的输入事件(年龄)
  input2Change(e) {
    this.setData({
      inputValue2: e.detail.value,
    });
  },
  tijiao(){
    wx.request({
      url: 'http://localhost:3000/user/user',
      method:'POST',
      data:{
        user_name:this.data.inputValue1,
        password:this.data.inputValue2
      },
    })
  },
  // 删除
  input1Change_del(e){
    this.setData({
      inputValue_del: e.detail.value,
    });
  },
  shanchu() {
    wx.request({
        url: 'http://localhost:3000/user/deluser1',
        method: 'POST',
        data: {
            user_name: this.data.inputValue_del,
        },
        success: (res) => {
            // 处理成功的情况
            console.log(res.data); // 可以打印后端返回的数据
        },
        fail: (error) => {
            // 处理失败的情况
            console.error(error);
        }
    })
},

  // 修改
  input1Change_upd(e){
    this.setData({
      inputValue1_upda: e.detail.value,
    });
  },
  input2Change_upd(e){
    this.setData({
      inputValue2_upda: e.detail.value,
    });
  },
  xiugai(){
    wx.request({
      url: 'http://localhost:3000/user/upd',
      method:'POST',
      data:{
        // 名字
        user_name:this.data.inputValue1_upda,
        // 修改后的年龄
        newPassword:this.data.inputValue2_upda,
      },
    })
  },
  // 查询
  input1Change_find(e){
    this.setData({
      inputValue1_find: e.detail.value,
    });
  },
  find(){
    wx.request({
      url: 'http://localhost:3000/user/find_one',
      method: 'POST',
      data:{
        // 名字
        user_name:this.data.inputValue1_find,
      },
      success: function(res) {
        // 请求成功,处理从服务器返回的数据
        console.log('服务器返回的数据:', res.data);

        // 检查是否找到匹配的记录
        if (res.data && res.data.length > 0) {
          // 处理返回的记录数据
          const records = res.data;
          records.forEach(record => {
            console.log('记录:', record);
            // 在这里进行您的处理逻辑,例如显示在界面上
          });
        } else {
          console.log('未找到匹配的记录');
          // 在界面上显示相应的消息,告知用户未找到匹配的记录
        }
      },
      fail: function(error) {
        // 请求失败,处理错误
        console.error('请求失败:', error);
        // 在界面上显示错误信息,告知用户请求失败
      }
    });
  },
  find_all(){
    wx.request({
      url: 'http://localhost:3000/user/users',
      method: 'GET',
      success: function(res) {
        // 请求成功,处理从服务器返回的数据
        console.log('服务器返回的数据:', res.data);

        // 检查是否找到匹配的记录
        if (res.data && res.data.length > 0) {
          // 处理返回的记录数据
          const records = res.data;
          records.forEach(record => {
            console.log('记录:', record);
            // 在这里进行您的处理逻辑,例如显示在界面上
          });
        } 
      },
      fail: function(error) {
        // 请求失败,处理错误
        console.error('请求失败:', error);
        // 在界面上显示错误信息,告知用户请求失败
      }
    });
  }
})