Nestjs入门及实战

1,083 阅读4分钟

前言

作为一个前端开发的我来说,如果想要了解后端,nodejs会是一个不错的选择;主要的原因是因为js/ts是我最熟悉的语言,同时我的目标是通过nodejs快速入门了解后端在项目上是如何解决业务问题的。大前端方向上关于nodejs的框架有不少,其中有express koa eggjs fastify Nestjs等,这里我选择的是Nestjs,数据库选择typeorm,前端选择umijs框架。要说原因的话,主要是Nestjs文档很详细,对我这种对后端不了解的人来说入门非常的友好,且它内置了大量的工具,开箱即用,能够帮助我快速入门,真的太香了。本篇文章的代码已经上传到github上,这是一个包含前后端代码的权限管理项目,目前已实现的功能有:创建项目、角色、用户、资源、给角色分配资源、给用户分配角色及接口权限;有兴趣可以了解,如果喜欢可以给我一个star。

0. nest g module/service/controller name

这一块内容可以先跳过,当你跟随着我的文章去手动敲一遍代码的话,你会发现手动一个一个敲真的好麻烦,并且当你要做的功能增加时,会引入更多的依赖包、各种包各种api容易搞混,慢慢会暴露出Nestjs框架的缺陷:各种api增加了学习负担。不过我们的目标是入门,了解Nestjs对业务项目的解决方案。
手动创建模块,很麻烦,可以通过nest g module/service/controller name的方式快速创建一个模块。 如果你定义的模块有文件层级,可以在name字段中输入 外层文件夹/name

1. 控制器Controller

定义

控制器使用 @Controller装饰器定义,@Controller()中可以定义路由前缀从而对一组路由进行分组;同一组路由可以通过@Get()和@Post来定义当前路由接收的是get请求还是post请求,并且可以在@Get()和@Post()中定义当前路由的具体路径,为空时路径则为@Controller()中的路径前缀,通过下面几个例子来帮助我们理解:

1. 路径为/admin同时可接收get和post两种请求

@Controller('admin')
export const Controller{
    @Get()
    async fn() {}

    @Post()
    async fn() {}
}

2. 路径为admin/demo的get请求

@Controller('admin')
export const Controller{
    @Get('demo')
    async create() {}
}

3. 动态参数路由

访问路径为 /admin/id

@Controller('admin')
export const Controller{
    @Get(':id')
    async create() {}
}

获取请求参数

Nest提供了一系列请求对象装饰器对象来获取前端的请求数据,常见的有:@Query()、@Body()、@Param()和@Res(),可以查看Nest官网文档了解更多,下面将简单介绍Nest中如何获取get、post请求参数和动态参数:

1. get请求 @Query()

语法一:使用@Query() query向路由注入参数

@Controller('admin')
export const Controller{
    @Get('demo')
    async create( @Query() query) {
        return { data: query }
    }
}


语法二:使用@Bind(Query())装饰器向路由注入参数
@Controller('admin')
export const Controller{
    @Get('demo')
    @Bind(Query())
    async create(query) {
        return { data: query }
    }
}

2. post参数

语法一:使用@Query() query向路由注入参数

@Controller('admin')
export const Controller{
    @Get('demo')
    async create( @Body() body) {
        return { data: body }
    }
}


语法二:使用@Bind(Query())装饰器向路由注入参数
@Controller('admin')
export const Controller{
    @Get('demo')
    @Bind(Body())
    async create(body) {
        return { data: body }
    }
}

3. 动态参数

访问路径为 /admin/id

@Controller('admin')
export const Controller{
    @Get(':id')
    @Bind(Param())
    async create(param) {
        return { id: param.id }
    }
}

设置响应状态码

在响应码这块,Nest内置两个装饰器@HttpCode()和@Res();@HttpCode()适用于静态状态码定义,但通常状态码不是静态的,因此我们可以使用@Res()装饰器根据需要来定义程序的状态码,下面将通过几个例子来介绍这两个装饰器的使用:

1. @HttpCode()静态状态码


@Controller('admin')
export const Controller{
    @Get()
    @HttpCode(200)
    async function(){}
}

当客户端请求 /admin 接口时,返回的数据格式是 
{
    code: 200
}

2. 动态设置 @Res

res.status(HttpStatus.CREATED).send(返回数据)


@Controller('admin')
export const Controller{
    @Get()
    async function(@Query() query, @Res() res){
        return res.statue(200).send({ code: 200, data: [], msg: '请求成功'})
    }
}

当客户端请求 /admin 接口时,返回的数据格式是 
{   
    status: 200,
    data: {
        code: 200,
        data: [],
        msg: '请求成功'
    }
}

2. services

services使用@Injectable()装饰器实现一个服务,通常对数据库进行增删改查胡操作。

services定义

需要先在.module模块providers中注入。可以注入多个(其它模块的service也可以注入)

@@filename('user.module')
import UserService from './user.service'
@Module({
    providers: [UserService]
})

services使用

controller中调用services


@@filename('user.controller')
import UserService from './user.service'

@Controller('user')
export default class UserController{
    constructor(private userService: UserService) {}

    @Post('login')
    async function login(@Body() body, @Res() res) {
        const result = await this.userService.login(body)
    }
}

3. Module模块

每个模块有自己的.service和.controller和.module,在.module模块中引入service(providers)和controller(controllers),最后在根模块 app.module中引入模块注册

@@filename('app.module')

import UserModule from './user/user.module'
@Module({
    imports: [UserModule]
})

共享模块(共享同一个实例)

@@filename('user.module')
@Module({
    exports: [UserService],
    controllers: [UserController],
    providers: [UserService]
})

现在任何导入UserModule的模块CatsModule都可以访问CatsService并且将与导入它的所有其他模块共享相同的实例

@@filename('role.module')
import UserModule from './user/user.module'
@Module({
    imports: [UserModule],
    controllers: [RoleController],
    providers: [RoleService]
})

@@filename('role.controller')

@Controller('role')
class RoleController{
    constructor( private roleService: RoleService) {}
}

导出导入的模块

@filename('user.module')
@Module({
    imports: [AdminModule],
    exports: [AdminModule]
})

全局模块

@Global()
@Module({})