当前位置: 首页 > 后端技术 > Node.js

[译文]基于TypeScript的Node.js框架Nest正式发布!(上)

时间:2023-04-03 17:22:32 Node.js

本文为翻译,原文地址:https://kamilmysliwiec.com/nest-final-release-is-here-node-js-framework-built-top-of-typescript,作者,@KamilMy?liwiecNest是一个功能强大的Node.jsWeb框架,可帮助您轻松构建高效、可扩展的应用程序。它基于现代JavaScript中的TypeScript构建,结合了OOP(面向对象编程)和FP(函数式编程)的最佳概念。它不仅仅是另一个框架。您不必等待大型社区,因为Nest建立在著名的存储库Express和socket.io之上。这意味着您可以快速开始使用该框架,而不必担心缺少第三方插件。核心概念Nest的核心概念是提供一种架构,帮助开发者实现层的最大分离,增加应用程序的抽象。安装Git:$gitclonehttps://github.com/kamilmysliwiec/nest-typescript-starter.git项目名称$cd项目名称$npminstall$npmrunstartNPM:$npmi--save@nestjs/core@nestjs/common@nestjs/microservices@nestjs/websockets@nestjs/testingreflect-metadatarxjs设置应用程序Nest使用ES6和ES7(装饰器、异步/等待)功能构建。这意味着,使用它的最简单方法是Babel或TypeScript。在本文中,我将使用TypeScript(这不是必需的!),我推荐它。示例文件tsconfig.json如下:":true,"target":"es6"},"exclude":["node_modules"]}请记住,emitDecoratorMetadata和experimentalDecorators必须设置为true。让我们从头开始我们的应用程序。首先,我们必须通过我们的应用程序创建入口模块(app.module.ts):import{Module}from'@nestjs/common';@Module({})exportclassApplicationModule{}此时,模块的元数据(metadata)为空({}),因为我们只想运行我们的应用程序而不加载任何控件或组件。在第二步中,创建文件index.ts并使用NestFactory创建基于我们模块类的Nest应用程序实例。从'@nestjs/core'导入{NestFactory};从'./app.module'导入{ApplicationModule};constapp=NestFactory.create(ApplicationModule);app.listen(3000,()=>console.log('应用程序正在侦听端口3000'));就是这样。Express实例如果你想完全控制express实例的生命周期,你可以简单地将创建的对象作为NestFactory.create()方法的第二个参数传递,如下所示:importexpressfrom'express';import{NestFactory}from'@nestjs/core';从'./modules/app.module'导入{ApplicationModule};constinstance=express();constapp=NestFactory.create(ApplicationModule,instance);app.listen(3000,()=>console.log('应用程序正在监听3000端口'));这意味着,您可以直接添加一些自定义配置(例如,设置一些插件,如morgan或body-parser)。控制器(Controllers)控制层(Controllers)负责处理传入的HTTP请求。在Nest中,控制器是一个带有@Controller()装饰器的类。在上一节中,我们设置了应用程序的入口点。现在,让我们构建我们的第一个文件路径/users:import{Controller,Get,Post}from'@nestjs/common';@Controller()exportclassUsersController{@Get('users')getAllUsers(){}@Get('users/:id')getUser(){}@Post('users')addUser(){}}正如你可以猜到,我们只是创建了一个有3个不同路径的路由:GET:usersGET:users/:idPOST:users没必要重复users的每一个路径吧?Nest允许我们将额外的元数据传递给@Controller()装饰器——路径,它是每个路由的前缀。让我们重写我们的控制器:@Controller('users')exportclassUsersController{@Get()getAllUsers(req,res,next){}@Get('/:id')getUser(req,res,next){}@Post()addUser(req,res,next){}}如您所见,Nest控制器中的方法与Express中的简单路由具有相同的参数列表和行为。如果想深入了解req(请求)、res(响应)和next,可以阅读简短的路由文档。在Nest中,它们是等价的。但是有一个重要的区别。Nest提供了一组自定义装饰器,您可以使用它们来标记参数。NestExpress@Request()req@Response()res@Next()next@Session()req.session@Param(param?:string)req.params[param]@Body(param?:string)req.body[param]@Query(param?:string)req.query[param]@Headers(param?:string)req.headers[param]你可以这样使用它们:@Get('/:id')publicasyncgetUser(@Response()res,@Param('id')id){constuser=awaitthis.usersService.getUser(id);res.status(HttpStatus.OK).json(user);}记得在文件device开头导入装饰器。import{Response,Param}from'@nestjs/common';UsersController可以工作,但我们的模块还不知道。让我们打开ApplicationModule并添加一些元数据。import{Module}from'@nestjs/common';import{UsersController}from"./users.controller";@Module({controllers:[UsersController]})exportclassApplicationModule{}如你所见,我们只需要import控制器被插入到控制器数组中,这就足够了。组件(Components)几乎一切都是组件,Service、Repository、Provider等等。它们可以通过构造函数注入到控制器或另一个组件中。在上一节中,我们构建了一个简单的控制器,UsersController。这个控制器可以访问我们的数据(我知道这是假数据,但没关系)。这不是一个很好的解决方案。我们的控制器只能处理HTTP请求并将更复杂的任务委托给服务,这就是我们创建UsersService组件的原因。实际上,UsersService应该从持久层调用适当的方法,例如,UsersRepository组件。我们没有任何类型的数据库,所以我们再次使用假数据。import{Component}from'@nestjs/common';import{HttpException}from'@nestjs/core';@Component()exportclassUsersService{privateusers=[{id:1,name:"JohnDoe"},{id:2,name:"AliceCaeiro"},{id:3,name:"谁知道"},];getAllUsers(){返回承诺。解决(这个。用户);}getUser(id:number){constuser=this.users.find((user)=>user.id===id);if(!user){thrownewHttpException("找不到用户",404);}返回Promise.resolve(用户);}addUser(用户){this.users.push(用户);返回Promise.resolve();Nest组件是一个用@Component()注解的简单类。正如您在getUser()方法中看到的,我们使用了HttpException。它是一个内置的Nest异常,有两个参数,一条错误消息和一个状态代码。创建域异常是一种很好的做法,它应该扩展HttpException(有关更多信息,请参阅“错误处理”一章)。我们的服务准备好了。让我们在UsersController中使用它。@Controller('users')exportclassUsersController{constructor(privateusersService:UsersService){}@Get()getAllUsers(@Responsereq){this.usersService.getAllUsers().then((users)=>res.status(HttpStatus.OK).json(用户));}@Get('/:id')getUser(@Response()res,@Param('id')id){this.usersService.getUser(+id).then((user)=>res.status(HttpStatus.OK).json(用户));}@Post()addUser(@Response()res,@Body('user')user){this.usersService.addUser(req.body.user).then((msg)=>res.status(HttpStatus.CREATED).json(味精));}}如图所示,UsersService将被注入到构造函数中。使用TypeScript管理依赖关系很容易,因为Nest会按类型识别您的依赖关系。像这样:constructor(privateusersService:UsersService)这就是你所要做的。一件更重要的事情是您必须在tsconfig.json中将emitDecoratorMetadata选项设置为true。如果你不是TypeScript爱好者,并且使用纯JavaScript,你必须这样做:import{Dependencies,Controller,Get,Post,Response,Param,Body,HttpStatus}from'@nestjs/common';@Controller('用户')@Dependencies(UsersService)exportclassUsersController{constructor(usersService){this.usersService=usersService;}@Get()getAllUsers(@Response()res){this.usersService.getAllUsers().then((users)=>res.status(HttpStatus.OK).json(users));}@Get('/:id')getUser(@Response()res,@Param('id')id){this.usersService.getUser(+id).then((user)=>res.status(HttpStatus.OK).json(用户));}@Post()addUser(@Response()res,@Body('user')user){this.usersService.addUser(user).then((msg)=>res.status(HttpStatus.CREATED).json(味精));}}这很容易,不是吗?目前,我们的应用程序还没有运行。为什么?因为Nest对UsersService一无所知。这个组件不是ApplicationModule的一部分,我们必须在那里添加:import{Module}from'@nestjs/common';import{UsersController}from'./users.controller';import{UsersService}from'./users.service';@Module({controllers:[UsersController],components:[UsersService],})exportclassApplicationModule{}现在我们的应用程序将执行,但仍然有一个路由不起作用,它是addUser。为什么?因为我们试图在不使用express的body-parser中间件的情况下解析请求主体(req.body.user)。如您所知,您可以将express实例作为第二个参数传递给NestFactory.create()方法。让我们安装插件:$npminstall--savebody-parser然后在我们的express实例中设置它。从'express'导入express;从'body-parser'导入*作为bodyParser;从'@nestjs/common'导入{NestFactory};从'./modules/app.module'导入{ApplicationModule};constinstance=express();instance.use(bodyParser.json());constapp=NestFactory.create(ApplicationModule,instance);app.listen(3000,()=>console.log('Applicationislisteningonport3000'));Async/awaitNest与ES7的async/await功能兼容。所以我们可以快速重写我们的UsersController:@Controller('users')exportclassUsersController{constructor(privateusersService:UsersService){}@Get()asyncgetAllUsers(@Response()res){constusers=awaitthis.usersService.getAllUsers();res.status(HttpStatus.OK).json(用户);}@Get('/:id')asyncgetUser(@Response()res,@Param('id')id){constuser=awaitthis.usersService.getUser(+id);res.status(HttpStatus.OK).json(用户);}@Post()asyncaddUser(@Response()res,@Body('user')user){constmsg=awaitthis.usersService.getUser(user);res.status(HttpStatus.CREATED).json(msg);}}这样看起来更好吗?您可以在此处阅读有关异步/等待的更多信息。模块模块是带有@Module({})装饰器的类。此装饰器提供框架用于组织应用程序结构的元数据。现在,这是我们的ApplicationModule:import{Module}from'@nestjs/common';从'./users.controller'导入{UsersController};从'./users.service'导入{UsersService};@Module({controllers:[UsersController],components:[UsersService],})exportclassApplicationModule{}默认情况下,模块封装了每个依赖项。这意味着无法在模块外部使用其组件或控制器。每个模块也可以导入到另一个模块中。事实上,您应该将Nest模块视为一棵模块树。我们将UsersController和UsersService移到了UsersModule。只需创建新文件,例如users.module.ts包含以下内容:import{Module}from'@nestjs/common';import{UsersController}from'./users.controller';import{UsersService}from'./users.service';@Module({controllers:[UsersController],components:[UsersService],})exportclassUsersModule{}然后将UsersModule导入ApplicationModule(我们的主应用程序):import{Module}from'@nestjs/common';从'./users/users.module'导入{UsersModule};@Module({modules:[UsersModule]})exportclassApplicationModule{}就这样。如您所见,使用Nest自然地将代码拆分为可分离和可重用的模块。依赖注入模块可以很容易地注入组件,看起来像这样:@Module({controllers:[UsersController],components:[UsersService,ChatGateway],})exportclassUsersModuleimplementsNestModule{constructor(privateusersService:UsersService){}}此外,该组件也可以注入到模块中:exportclassUsersController{constructor(privatemodule:UsersModule){}}下一个链接:https://segmentfault.com/a/1190000009573441