使用Nest和Typescript创建Node.js REST API
在Twitter上关注我,很高兴听取您关于主题或改进的建议/ Chris
用于构建高效,可靠和可扩展的服务器端应用程序的渐进式Node.js框架。
在本文中,我们将介绍Nest库。一个使创作API成为非常好的体验的库。如果你来自Angular世界,你一定会认识到自己使用的概念,一个很棒的CLI,当然还有很好的用法。
注意,它虽然不是Angular,但是以尽可能最好的方式非常接近。
本文是关于Nest的一系列文章的一部分,因为我们无法在一篇文章中涵盖所有内容。
我们将介绍以下内容:
- 为什么选择Nest,让我们来看看销售情况,并提及使Nest成为您下一个API的绝佳选择的功能
- 你的第一个CRUD项目 – 涵盖基础知识,让我们构建一个项目并重温基本结构
为何选择Nest
我们来看看主页上的销售情况
- 可扩展,借助模块化架构,允许使用任何其他库
- 多功能,适应性强的生态系统,适用于各种服务器端应用
- Progressive,利用最新的JavaScript功能,设计模式和成熟的解决方案
好吧,这一切听起来都不错,但给我一些我可以和同事一起玩的东西
它完全支持TypeScript,但如果您愿意,可以使用纯JavaScript。
它使用库 表达
和 Fastify
引擎盖下,但如果需要也可以暴露他们的API。
听起来很有意思,告诉我更多
它附带一个CLI,因此您可以构建一个项目以及添加工件。
真好
最重要的是,您可以使用Jest轻松编写单元测试以及E2E测试,并且可以使用它轻松构建GraphQL API
停下来,你正在努力
不是,请查看Nest和GraphQL
资源
我们将在本文中提到一些很好的资源。如果你错过我们提到的链接,这里就是。
- 官方文档页面官方文档页面是一个很好的页面。它涵盖了从基础知识到食谱的所有内容
- 概述部分whol概述部分是一个很好的阅读,试图理解核心概念,你也可以使用CLI来支撑项目
- 食谱那里有很多很好的食谱。从如何使用不同的ORM到设置Swagger(这是超级简单顺便说一句)的一切
您的第一个项目 – 涵盖基础知识
那好吧。我们开工吧。在我们开始创建第一个项目之前,我们需要CLI来创建和运行我们的项目以及更多的东西。我们可以使用以下命令轻松安装CLI:
npm i -g @ nestjs / cli
接下来我们需要建立一个项目。接下来让我们这样做:
嵌套新的你好世界
你可以替换 你好,世界
使用您选择的项目名称。
好的,我们得到了很多文件。从上面的图像判断,我们似乎得到了一个Node.js项目 的package.json
并且使用Jest设置了一些测试,当然还有一些看似Nest特有的工件,如控制器,模块和服务。让我们仔细看看脚手架项目:
它是如何工作的?
在我们运行项目之前,我们只是搭建脚手架,让我们先仔细看看,以便了解生命周期。首先让我们来看看 main.ts
。这是我们的应用程序的切入点。更具体地说,它是 引导()
通过运行代码启动所有内容的方法:
// main.ts 常量 应用 = 等待 NestFactory。创建(的AppModule); 等待 应用。听(3000);
好的,所以 NestFactory
电话 创建()
实例化 的AppModule
我们得到一个 应用
似乎在端口上侦听的实例 3000
。让我们去 的AppModule
看看那里发生了什么:
//app.module.ts @模({ 进口: (), 控制器: (AppController的) 供应商: (AppService服务) }) 出口 类 的AppModule {}
好吧,我们似乎有一堂课 的AppModule
正在装饰的 @Module
装饰者,具体的控制器 AppController的
以及被归类为提供者的东西 AppService服务
。
这一切如何运作?
好吧,控制器 AppController的
响应路由请求,让我们看看如何设置:
// app.controller.ts @调节器() 出口 类 AppController的 { 构造函数(私人的 只读 AppService服务: AppService服务) {} @得到() getHello(): 串 { 返回 这个。AppService服务。getHello(); } }
装饰者 @得到()
确保我们将某个GET请求映射到我们班级的某个方法。在这种情况下,默认路由 /
将采用该方法回应 getHello()
而这又调用了 appService.getHello()
。我们来看看吧 app.service.ts
:
// app.service.ts 进口 { 注射 } 从 '@ nestjs /共同'; @注射() 出口 类 AppService服务 { getHello(): 串 { 返回 '你好,世界'; } }
这似乎是一个非常简单的类方法 getHello()
返回一个字符串。
现在,让我们回过头来 app.controller.ts
。
从我们所看到的 AppService服务
正在注入构造函数 AppController的
像这样:
//摘自app.controller.ts 构造函数(私人的 只读 AppService服务: AppService服务) {}
它是如何知道如何做到的?
这里有两个答案:
- 如果添加
注射()
装饰器到任何服务,这意味着它可以注入其他工件,如控制器或服务。 - 这将我们带到第二步。我们需要将所述服务添加到
供应商
用于使DI机器工作的模块的阵列。
哦?
是的,让我们尝试通过添加新路线的动作来巩固这种理解。但在我们这样做之前,让我们开始这个项目并证明它的工作原理就像我们说的那样:
npm开始
现在,让我们前往浏览器:
添加路线
我们刚刚学会了支撑一个项目,并学会了运行同样的东西。我们认为我们对概念模块,控制器和服务有一个很好的把握,但没有任何东西可以巩固这些知识,就像添加新路径并添加我们需要的所有工件一样。我们将执行以下操作:
我们将创建一条新路线 /产品
为此,我们需要执行以下步骤
- 添加新服务
- 添加一个新控制器并注入我们的服务
- 连接DI机制
- 运行我们的应用程序,确保一切正常
我们要做的第一件事是学习如何正确使用Nest项目。现在我们跑了 npm开始
编译我们的TypeScript代码并在端口托管我们的应用程序 3000
但在开发过程中,我们可能想要一些能够自动监听变化和编译的东西。为此,请改为运行命令 npm run start:dev
,在需要时监听更改并重新编译。
npm run start:dev
注意,在我们开始使用上面的命令之前,让我们支持所有需要的文件,然后我们可以运行上面的内容,以便我们在特定的代码文件中查看并且我们希望我们的更改能够反映出来。
创建服务
让我们创造我们的产品服务。现在,将数据设为静态,我们可以稍后再查看添加HTTP调用。让我们以Nest方式做事并使用CLI
nest生成服务产品
或者更短的版本
nest g s产品
好的,打开文件 产品/ products.service.ts
。它应该是这样的:
进口 { 注射 } 从 '@ nestjs /共同'; @注射() 出口 类 ProductsService {}
现在添加方法 的getProducts()
所以它现在看起来像这样:
进口 { 注射 } 从 '@ nestjs /共同'; @注射() 出口 类 ProductsService { 的getProducts() { 返回 ({ ID: 1, 名称: 'SPA应用' }, { ID: 2, 名称: 'Nest API' }) } }
添加控制器
现在是创建我们的控制器的时候了,所以让我们接下来做。我们再一次只是CLI,如下所示:
nest生成控制器产品
或者,更短的版本
nest g co products
打开 产品/ products.controller
:
进口 { 调节器 } 从 '@ nestjs /共同'; @调节器(“产品”) 出口 类 的ProductsController {}
下一步是添加方法 的getProducts()
并确保我们称之为我们的服务,当然我们不会忘记用它来装饰它 @得到()
装饰。
您的代码现在应该如下所示:
进口 { 调节器, 得到 } 从 '@ nestjs /共同'; 进口 { ProductsService } 从 './products.service'; @调节器(“产品”) 出口 类 的ProductsController { 构造函数(私人的 productsService: ProductsService) {} @得到() 的getProducts() { 返回 这个。productsService。的getProducts(); } }
我们来试试吧:
npm run start:dev
上面我们可以看到我们的 /产品
似乎已添加了路线 的ProductsController
将响应该路线上的任何请求。但是怎么可能,我们没有做任何事情 app.module.ts
连接DI,还是我们?
我们来看看吧 app.module.ts
:
我们可以在上面看到 的ProductsController
和 ProductsService
已添加到 控制器
和 供应商
分别。当我们生成控制器和服务时,CLI为我们添加了它。
我们几乎忘记了在浏览器中运行我们的应用程序的东西,所以让我们这样做:
注意,CLI功能强大,它不仅会创建必要的文件,还会进行一些连接,但是如果您不使用CLI,则需要知道需要做什么。
添加剩余的CRUD路由
好的,所以我们添加了一条支持路线 /产品
路线。众所周知,虽然我们需要更多的路线 POST
, 放
, 删除
和通配路线等
我们如何添加这些?
很简单,我们只需为每个人创建方法,并添加装饰器来支持它,如下所示:
// products.controller.ts 进口 { 调节器, 得到, 帕拉姆, 岗位, 身体, 放, 删除 } 从 '@ nestjs /共同'; 进口 { ProductsService } 从 './products.service'; 接口 ProductDto { ID: 串; 名称: 串; } @调节器(“产品”) 出口 类 的ProductsController { 构造函数(私人的 productsService: ProductsService) {} @得到() 的getProducts() { 返回 这个。productsService。的getProducts(); } @得到(':ID') getProduct(@帕拉姆() PARAMS) { 安慰。日志('获得单一产品', PARAMS。ID); 返回 这个。productsService。的getProducts()。过滤(p => p。ID == PARAMS。ID); } @岗位() createProduct(@身体() 产品: ProductDto) { 安慰。日志('创造产品', 产品); 这个。productsService。createProduct(产品); } @放() 的UpdateProduct(@身体() 产品: ProductDto) { 安慰。日志('更新产品', 产品); 这个。productsService。的UpdateProduct(产品); } @删除() deleteProduct(@身体() 产品: ProductDto) { 安慰。日志('删除产品', 产品。ID); 这个。productsService。deleteProduct(产品。ID); } }
和 products.service.ts
现在看起来像这样:
进口 { 注射 } 从 '@ nestjs /共同'; @注射() 出口 类 ProductsService { 制品 = ({ ID: 1, 名称: 'SPA应用' }, { ID: 2, 名称: 'Nest API' }); 的getProducts() { 返回 这个。制品; } createProduct(产品) { 这个。制品 = (...这个。制品, {...产品}); } 的UpdateProduct(产品) { 这个。制品 = 这个。制品。地图(p => { 如果 (p。ID == 产品。ID) { 返回 { ...产品}; } 返回 p; }); } deleteProduct(ID) { 这个。制品 = 这个。制品。过滤(p => p。ID = ID); } }
摘要
希望您现在已经意识到Nest的结构是否良好以及创建API和读取查询参数以及支持完整CRUD API的主体是多么容易。我们还介绍了CLI,它真正是您生成所需代码的最佳朋友,并确保您不需要考虑如何连接。
在下一部分中,我们将看看如何测试我们的代码,这是一个真正的幸福体验。所以请继续关注。