1. Initialization

The installation

npm i -g @nestjs/cli
nest new nest-mongo
Copy the code

Delete the test

  • Remove package.json test dependencies
// Here is the deleted code
{
    "scripts": {
        "test": "jest"."test:watch": "jest --watch"."test:cov": "jest --coverage"."test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand"."test:e2e": "jest --config ./test/jest-e2e.json"
    },
    "devDependencies": {
        "@nestjs/testing": "^ 7.6.15"."@types/supertest": "^ 2.0.10." "."@types/jest": "^ 26.0.22"."supertest": "^ also 6.1.3." "."ts-jest": "^ 26.5.4",},"jest": {... }}Copy the code
  • Deleting a Spec File

  • Installation operation

$ npm install
$ npm run start

# Hello World! successful
Copy the code

Custom formatting

// .pretterrc
{
    "jsxSingleQuote": true."jsxBracketSameLine": true."printWidth": 120."singleQuote": true."trailingComma": "none"."useTabs": true."tabWidth": 2."semi": false."arrowParens": "avoid"
}
Copy the code
$ npm run lint
Copy the code

2. Login interface

Configure the mongodb user tableusers connections, add a few users

Access to the Mongo

$ npm install --save @nestjs/mongoose mongoose
Copy the code
// app.module.ts
import { MongooseModule } from '@nestjs/mongoose'

@Module({
    imports: [
        MongooseModule.forRoot(
                'mongodb://
      
       ? authSource=admin&retryWrites=true&w=majority'
      ,
                { useFindAndModify: false }
                // Eliminate warnings (early Mongoose used the later mongodb method, now do synchronization))],... })Copy the code

The users module

# create users directory, create three modules
src/users/users.module.ts # into the app. The module. Ts
src/users/users.controller.ts Add a /user/login interface
src/users/users.service.ts 
Copy the code
// src/users/users.controller.ts
import { Controller, Post } from '@nestjs/common'
import { UsersService } from './users.service'

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

    @Post('login')
    login() {
        return 'login api'}}Copy the code

Accessing the database

// The user module injects mongodb
// src/users/users.module.ts
import { MongooseModule } from '@nestjs/mongoose'
import { UserSchema } from './schemas/user.schema'

@Module({
    imports: [MongooseModule.forFeature([{ name: 'users'.schema: UserSchema }])],
})
Copy the code
/ / user structure
// src/users/schemas/user.schema.ts
import { Prop, SchemaFactory, Schema } from '@nestjs/mongoose'
import { Document } from 'mongoose'

@Schema(a)export class User extends Document {
    @Prop()
    ...
}
export const UserSchema = SchemaFactory.createForClass(User)
Copy the code
// user data service
// scr/users/users.service.ts
import { Injectable } from '@nestjs/common'
import { LeanDocument, Model } from 'mongoose'
import { InjectModel } from '@nestjs/mongoose'
import { User } from './schemas/user.schema'

Injectable()
export class UsersService {
    constructor(@InjectModel('users') private readonly usersModel: Model<User>) {}

    async validateUser(username: string.password: string) :Promise<Omit<LeanDocument<User>, 'password'> | void> {
        const user = await this.usersModel.findOne({ username }).exec()
        if (user && user.password === password) {
            const{ password, ... result } = user.toObject({getters: true })
            return result
        }
    }
}
Copy the code
// user login route import {Controller, Post, Body } from '@nestjs/common' import { UsersService } from './users.service' @Controller('user') export class UsersController { constructor(private usersService: UsersService) {} @Post('login') async login(@Body() loginDto: { username: string; password: string }) { const user = await this.usersService.validateUser(loginDto.username, Logindto.password) if (user) return user else return 'Please enter'}}Copy the code

JWT generated token

$ npm install @nestjs/jwt passport-jwt
$ npm install @types/passport-jwt --save-dev # passport- JWT does not have ts declaration file yet
Copy the code
// The user module injects the JWT module
// src/users/users.module.ts
import { JwtModule } from '@nestjs/jwt'
import { jwtContants } from './contants' // Create a constant file for other files to reference

@Module({
    imports: [
        JwtModule.register({
            secret: jwtContants.secret,
            signOptions: { expiresIn: '1d'}})],...Copy the code
// user Logs in to the service
// src/users/users.service.ts
import { JwtService } from '@nestjs/jwt'

Injectable()
export class UsersService {
    constructor(
        @InjectModel('users') private readonly usersModel: Model<User>,
        private readonly jwtService: JwtService
    ){}... login(user: LeanDocument<Omit<User,'password'> >) :string {
        const payload = { username: user.username, id: user._id }
        return 'Bearer ' + this.jwtService.sign(payload)
    }
}
Copy the code
// src/users/users.controller.ts
@Controller('user')
    @Post('login')
    async login(@Body() loginDto: { username: string; password: string }) {
        const user = await this.usersService.validateUser(loginDto.username, loginDto.password)
        if (user) return this.usersService.login(user)
    }
Copy the code

User authentication

// JWT policy file
// src/users/strategies/jwt.strategy.ts
import { Injectable } from '@nestjs/common'
import { PassportStrategy } from '@nestjs/passport'
import { Strategy, ExtractJwt } from 'passport-jwt'
import { jwtContants } from '.. /constants'

@Injectable(a)export class JwtStrategy extends PassportStrategy(Strategy) {
    constructor() {
        super({
            jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
            ignoreExpiration: false.secretOrKey: jwtContants.secret
        })
    }

    validate(payload) {
        return { id: payload.id, username: payload.username }
    }
}
Copy the code
// The user module injects JWT policy
// src/users/users.module.ts
import { JwtStrategy } from './strategies/jwt.strategy'
@Module({
    providers: [UsersService, JwtStrategy],
    ...
})
export class UsersModule {}
Copy the code
// user Queries the user data service
// src/users/users.service.ts
Injectable()
export class UsersService {...async findById(id: string) :Promise<Partial<User> | void> {
        const user = this.usersModel.findById(id).exec()
        if (user) {
            const{ password, id, _id, ... result } = (await user).toObject({ getters: true })
            return result
        }
    }
}
Copy the code
// Query user routes
// src/users/users.controller.ts
@Controller('user')
export class UsersController {...@UseGuards(AuthGuard('jwt'))
    @Get('profile')
    async getProfile(@Request() req) {
        const user = await this.usersService.findById(req.user.id)
        if (user) return user
    }
}
Copy the code

Package abnormal

// Nest JWT automatically returns 401 if token verification fails
{
    "statusCode": 401."message": "Unauthorized"
}

// The statusCode field is not what I want, I need to change it to the usual code
Copy the code
// Create an exception filter
// src/common/HttpExceptionFilter.ts
import { ArgumentsHost, Catch, ExceptionFilter, HttpException } from '@nestjs/common'
import { Response } from 'express'

@Catch(a)export class HttpExceptionFilter implements ExceptionFilter {
    catch(exception: HttpException, host: ArgumentsHost) {
        const ctx = host.switchToHttp()
        const response = ctx.getResponse<Response>()
        const status = exception.getStatus()

        response.status(status).json({
            code: status,
            message: exception.message
        })
    }
}
Copy the code
// Global binding
// src/main.ts
import { HttpExceptionFilter } from './common/HttpExceptionFilter'. app.useGlobalFilters(new HttpExceptionFilter())
...
Copy the code

Transmission validation pipeline

$ npm install class-validator class-transformer
Copy the code
// src/users/dto/login.dto
import { IsDefined, IsString, Length } from 'class-validator'

export class LoginDto {... }Copy the code
// Bind validation
// src/users/users.controller.ts
import { LoginDto } from './dto/login.dto'.async login(@Body() loginDto: LoginDto){...Copy the code

Return error data not displayed correctly. Modify HttpExceptionFilter

// src/common/HttpExceptionFilter.ts.const resOfException: string| { message? :string } = exception.getResponse()

    response.status(status).json({
        code: status,
        message: typeof resOfException === 'string' ? resOfException : resOfException.message
    })
...
Copy the code

Set return structure

// src/common/types/return.ts
export interface Return {
    code: number

    message: string

    date: any
}
Copy the code

3. Log routes

The new file

# Build diaries module

src/diaries/diaries.module.ts
src/diaries/diaries.service.ts
src/diaries/diaries.controller.ts

App => Diaries => Service + Controller
Copy the code
// Test the route
import { Controller, Get } from '@nestjs/common'

@Controller('diaries')
export class DiariesController {
    @Get(a)getDiaries() {
        return 'get diaries'}}Copy the code

Linked data table

// Create a new data structure
// src/diaries/schemas/diary.schema.ts
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose'
import { Document } from 'mongoose'

@Schema(a)export class Diary extends Document {... }export const DiarySchema = SchemaFactory.createForClass(Diary)
Copy the code
// Module links to diaries table
// src/diaries/diaries.module.ts
import { MongooseModule } from '@nestjs/mongoose'
import { DiarySchema } from './schemas/diary.schema'
@Module({
    imports: [MongooseModule.forFeature([{ name: 'diaries'.schema: DiarySchema }])],
})
export class DiariesModule {}
Copy the code
// Data services
// src/diaries/diaries.service.ts
import { Injectable } from '@nestjs/common'
import { InjectModel } from '@nestjs/mongoose'
import { LeanDocument, Model } from 'mongoose'
import { Diary } from './schemas/diary.schema'

@Injectable(a)export class DiariesService {
    constructor(@InjectModel('diaries') private readonly diariesModel: Model<Diary>) {}

    async findAll(pageSize = 10, pageNum = 1) :Promise<LeanDocument<Diary>[]> {
        const diaries = await this.diariesModel
            .find()
            .limit(pageSize)
            .skip((pageNum - 1) * pageSize)
            .exec()
        const diariesObject = diaries.map(diary= > diary.toObject())
        return diariesObject
    }
}
Copy the code
// The route returns data
// src/diaries/diaries.controller.ts

@Controller('diaries')
export class DiariesController {...@Get(a)async getDiaries() {
        const diaries = this.diariesService.findAll()
        return diaries
    }
...
Copy the code
  • The login limit
// src/diaries/diaries.controller.ts

@UseGuards(AuthGuard('jwt'))
@Controller('diaries')
export class DiariesController {...@Get(a)async getDiaries(@Request() req): Promise<ReturnType> {
        const diaries = this.diariesService.findAll(req.user.id) ... }}Copy the code
  • Data screening
// src/diaries/diaries.service.ts.async findAll(id: string, pageSize = 10, pageNum = 1) :Promise<LeanDocument<Diary>[]> {
    ...
    .find({ author: id })
    ...
}
Copy the code