使用 Nest 和 Typescript 创建您的第一个 Node.js REST API
在Twitter上关注我,很高兴接受您对主题或改进的建议/Chris
用于构建高效、可靠且可扩展的服务器端应用程序的渐进式 Node.js 框架。
在本文中,我们将了解 Nest 库。这个库让编写 API 成为一种非常愉快的体验。如果您来自 Angular 世界,您一定会对它所使用的概念、强大的 CLI 以及 Typescript 的出色运用感到熟悉。
请注意,虽然它不是 Angular,但是以最好的方式来说,它非常接近 Angular。
本文是 Nest 系列文章的一部分,因为我们不可能在一篇文章中涵盖所有内容。
我们将介绍以下内容:
- 为什么选择 Nest?让我们来看看它的销售宣传,并了解 Nest 有哪些特性,使其成为您下一个 API 的绝佳选择。
- 您的第一个 CRUD 项目 - 涵盖基础知识,让我们搭建一个项目并讨论基本构造
为什么选择 Nest
让我们看看主页上的销售宣传
- 可扩展,得益于模块化架构,允许使用任何其他库
- 多功能,适应性强的生态系统,适用于各种服务器端应用程序
- 渐进式,利用最新的 JavaScript 功能、设计模式和成熟的解决方案
好吧,听起来不错,但请给我一些能让同事们惊叹的东西
它完全支持 TypeScript,但如果您愿意,也可以使用纯 JavaScript。
它使用库Express
和Fastify
底层功能,但如果需要也可以公开它们的 API。
听起来很有趣,告诉我更多
它带有一个 CLI,因此您可以搭建项目以及添加工件。
那很好
最重要的是,你可以使用 Jest 轻松编写单元测试和 E2E 测试,并且可以轻松地用它构建 GraphQL API
别说了,你胡编乱造
不,看看Nest 和 GraphQL
资源
我们将在本文中提及一些很棒的资源。如果您错过了我们提到的链接,可以在这里找到。
- 官方文档页面官方文档页面是一个很好的入门页面。它涵盖了从基础知识到食谱的所有内容。
- 概述部分整个概述部分非常值得一读,可以帮助您理解核心概念,并且还可以使用 CLI 来搭建项目
-
食谱
里面有很多好食谱。从如何使用不同的 ORM 到设置 Swagger(顺便说一下,超级简单),应有尽有。 -
通过简单的 NPM 安装将 Nest 部署到无服务器。- 然后看看这里如何部署 Azure 函数。
- 您将需要一个 Azure 帐户,免费帐户 Azure 帐户
你的第一个项目 - 涵盖基础知识
好的,那就开始吧。在开始创建我们的第一个项目之前,我们需要 CLI 来创建和运行我们的项目以及执行其他许多操作。我们可以使用以下命令轻松安装 CLI:
npm i -g @nestjs/cli
接下来我们需要搭建一个项目。接下来我们开始吧:
nest new hello-world
您可以hello-world
用您选择的项目名称进行替换。
好的,我们得到了很多文件。从上面的图片来看,我们似乎已经得到了一个 Node.js 项目,package.json
并用 Jest 设置了一些测试,当然还有一些看起来像 Nest 特有的构件,比如controller、module和service。让我们仔细看看这个搭建好的项目:
它是如何工作的?
在运行我们刚刚搭建的项目之前,我们先来仔细看看它的生命周期。首先,我们来看一下main.ts
。这是我们应用的入口点。更具体地说,它是bootstrap()
通过运行以下代码来启动所有程序的方法:
// main.ts
const app = await NestFactory.create(AppModule);
await app.listen(3000);
好的,NestFactory
调用create()
来实例化 ,AppModule
我们得到一个app
似乎在监听端口 的实例3000
。让我们去AppModule
看看那里发生了什么:
//app.module.ts
@Module({
imports: [],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
好的,我们似乎有一个AppModule
被@Module
装饰器装饰的类,该类指定了一个控制器AppController
和一些被归类为提供者的东西AppService
。
这一切是如何运作的?
好吧,控制器AppController
响应路由请求,所以让我们看看如何设置它:
// app.controller.ts
@Controller()
export class AppController {
constructor(private readonly appService: AppService) {}
@Get()
getHello(): string {
return this.appService.getHello();
}
}
装饰器@Get()
确保我们将某个 GET 请求映射到类上的某个方法。在本例中,默认路由/
将使用该方法进行响应,getHello()
该方法又会调用appService.getHello()
。我们来看看app.service.ts
:
// app.service.ts
import { Injectable } from '@nestjs/common';
@Injectable()
export class AppService {
getHello(): string {
return 'Hello World!';
}
}
这似乎是一个非常简单的类,具有getHello()
返回字符串的方法。
现在,让我们回到app.controller.ts
。
从我们所看到的,appService
它是在构造函数中注入的,AppController
就像这样:
// excerpt from app.controller.ts
constructor(private readonly appService: AppService) {}
它怎么知道该怎么做呢?
这里有两个答案:
- 如果将装饰器添加
Injectable()
到任何服务,则意味着它可以注入其他工件,例如控制器或服务。 - 这引出了第二步。我们需要将上述服务添加到
providers
模块的数组中,以使 DI 机制正常工作。
哦?
是的,让我们试着通过添加一条新路线来巩固一下理解。但在此之前,我们先启动这个项目,并验证它是否真的像我们说的那样有效:
npm start
现在,让我们进入浏览器:
添加路线
我们刚刚学会了如何搭建一个项目,以及如何运行它。我们自认为对模块、控制器和服务的概念已经有了相当的理解,但没有什么比添加一条新路线并添加所有必要的构件更能巩固这些知识了。我们将进行以下操作:
我们将创建一条新路线/products
,为此,我们需要执行以下步骤
- 添加新服务
- 添加新的控制器并注入我们的服务
- 连接 DI 机制
- 运行我们的应用程序并确保一切正常。
我们要做的第一件事是学习如何正确使用 Nest 项目。目前,我们运行了npm start
,它编译了 TypeScript 代码并将我们的应用程序托管在端口上,3000
但在开发过程中,我们可能需要一些能够监听更改并自动编译的功能。为此,我们改为运行命令npm run start:dev
,它会监听更改并在需要时重新编译。
npm run start:dev
注意,在我们开始使用上述命令之前,让我们先搭建所有需要的文件,然后当我们在特定的代码文件中乱搞并希望我们的更改能够反映出来时,我们可以运行上述命令。
创建服务
让我们创建产品服务。现在,先将数据设为静态,稍后再考虑添加 HTTP 调用。让我们按照 Nest 的方式操作,并使用 CLI
nest generate service products
或较短的版本
nest g s products
好的,打开文件products/products.service.ts
。它应该如下所示:
import { Injectable } from '@nestjs/common';
@Injectable()
export class ProductsService {}
现在添加方法getProducts()
,它现在看起来像这样:
import { Injectable } from '@nestjs/common';
@Injectable()
export class ProductsService {
getProducts() {
return [{
id: 1,
name: 'A SPA app'
},
{
id: 2,
name: 'A Nest API'
}]
}
}
添加控制器
现在该创建控制器了,接下来我们来做。同样,我们只需要使用 CLI,如下所示:
nest generate controller products
或,较短版本
nest g co products
打开products/products.controller
:
import { Controller } from '@nestjs/common';
@Controller('products')
export class ProductsController {}
下一步是添加一个方法getProducts()
并确保我们调用我们的服务,当然我们不要忘记用@Get()
装饰器来装饰它。
您的代码现在应如下所示:
import { Controller, Get } from '@nestjs/common';
import { ProductsService } from './products.service';
@Controller('products')
export class ProductsController {
constructor(private productsService: ProductsService) {}
@Get()
getProducts() {
return this.productsService.getProducts();
}
}
让我们尝试一下:
npm run start:dev
上面我们可以看到我们的/products
路由似乎已经添加,并且ProductsController
会响应该路由上的任何请求。但这怎么可能呢?我们还没有做任何app.module.ts
连接 DI 的操作,对吧?
让我们看看app.module.ts
:
我们可以看到ProductsController
, 和ProductsService
分别被添加到controllers
和providers
中。CLI 在生成控制器和服务时就已经帮我们添加了它。
我们几乎忘记了在浏览器中运行我们的应用程序的某些东西,所以让我们这样做:
注意,CLI 功能强大,它不仅可以创建必要的文件,还可以进行一些连接,但如果您不使用 CLI,它知道您需要做什么。
添加剩余的 CRUD 路由
好的,我们添加了一个路由来支持/products
路由。不过众所周知,我们需要的路由远不止这些POST
,例如PUT
,,DELETE
以及通配符路由等等。
我们如何添加它们?
很简单,我们只需要为每个人创建方法并添加装饰器来支持它,如下所示:
// products.controller.ts
import { Controller, Get, Param, Post, Body, Put, Delete } from '@nestjs/common';
import { ProductsService } from './products.service';
interface ProductDto {
id: string;
name: string;
}
@Controller('products')
export class ProductsController {
constructor(private productsService: ProductsService) {}
@Get()
getProducts() {
return this.productsService.getProducts();
}
@Get(':id')
getProduct(@Param() params) {
console.log('get a single product', params.id);
return this.productsService.getProducts().filter(p => p.id == params.id);
}
@Post()
createProduct(@Body() product: ProductDto) {
console.log('create product', product);
this.productsService.createProduct(product);
}
@Put()
updateProduct(@Body() product: ProductDto) {
console.log('update product', product);
this.productsService.updateProduct(product);
}
@Delete()
deleteProduct(@Body() product: ProductDto) {
console.log('delete product', product.id);
this.productsService.deleteProduct(product.id);
}
}
现在products.service.ts
看起来像这样:
import { Injectable } from '@nestjs/common';
@Injectable()
export class ProductsService {
products = [{
id: 1,
name: 'A SPA app'
},
{
id: 2,
name: 'A Nest API'
}];
getProducts() {
return this.products;
}
createProduct(product) {
this.products = [...this.products, {...product}];
}
updateProduct(product) {
this.products = this.products.map(p => {
if (p.id == product.id) {
return { ...product};
}
return p;
});
}
deleteProduct(id) {
this.products = this.products.filter(p => p.id != id);
}
}
概括
希望您现在已经意识到 Nest 的结构是多么完善,以及创建 API 并读取查询参数和主体以支持完整的 CRUD API 是多么容易。我们还引入了 CLI,它是您生成所需代码的最佳助手,确保您无需考虑如何连接。
下一部分我们将探讨如何测试代码,这将是一次真正令人愉悦的体验。敬请期待。
文章来源:https://dev.to/itnext/nest-creating-a-rest-api-has-never-felt-so-good-4i1