nest.js_使用Nest.js,MongoDB和Vue.js构建现代应用

nest.js

介绍 ( Introduction )

Nest.js introduces a modern way of building Node.js apps by giving it a proper and modular structure out of the box. It was fully built with TypeScript but still preserves compatibility with plain JavaScript.

Nest.js通过提供一种现成的,适当的模块化结构,引入了一种构建Node.js应用程序的现代方法。 它完全使用TypeScript构建,但仍保留了与普通JavaScript的兼容性。

In this post, I will introduce and explain the fundamental steps to follow in order to combine this awesome framework with a modern frontend JavaScript framework such as Vue.js. You will build a web application to manage customers information. The application will be used to create a new customer, add several details about the customer and update each customer's records in the database.

在这篇文章中,我将介绍和解释将基本框架与现代前端JavaScript框架(如Vue.js)结合起来所要遵循的基本步骤。 您将构建一个Web应用程序来管理客户信息。 该应用程序将用于创建新客户,添加有关该客户的几个详细信息以及更新数据库中每个客户的记录。

The approach to this post will be to build a separate REST API backend with Nest.js and a frontend to consume this API using Vue.js. So basically, instead of building Nest.js application that uses a Node.js template engine for the client side, you will leverage on the awesomeness of Vue.js as a progressive JavaScript library to quickly render contents and handled every client-side related logic.

这篇文章的方法将是使用Nest.js构建一个单独的REST API后端,以及使用Vue.js使用该API的前端。 因此,基本上,您无需构建使用客户端使用Node.js模板引擎的Nest.js应用程序,而是将Vue.js的强大功能作为渐进式JavaScript库来快速呈现内容并处理与客户端相关的所有逻辑。

In addition, you will use MongoDB database to persist and retrieve data for the application. MongoDB is a schema-less NoSQL document database that can receive and store data in JSON document format. It is often use with Mongoose; an Object Data Modeling (ODM) library, that helps to manage relationships between data and provides schema validations. You learn more about this later in this tutorial.

此外,您将使用MongoDB数据库来保留和检索应用程序的数据。 MongoDB是一个无模式的NoSQL文档数据库,可以接收和存储JSON文档格式的数据。 它经常与猫鼬一起使用; 对象数据建模(ODM)库,该库有助于管理数据之间的关系并提供架构验证。 您将在本教程的后面部分了解有关此内容的更多信息。

先决条件 ( Prerequisites )

  • A reasonable knowledge of building applications with JavaScript is required and a basic knowledge of TypeScript will be an added advantage.

    需要具有使用JavaScript构建应用程序的合理知识,并且TypeScript的基本知识将是附加的优势。

  • Ensure that you have Node and npm installed on your local system. Check this link for Node and here for instructions on how to install npm.

    确保在本地系统上安装了Node和npm。 在Node上检查此链接,在此处获得有关如何安装npm的说明。

  • Read this article here on scotch.io to grasp the fundamental knowledge of the building blocks of Nest.js.

    在scotch.io上阅读此文章,以掌握Nest.js构建基块的基本知识。

  • Install MongoDB on your local system. Follow the instructions here to download and installed it for your choice of operating system. This tutorial uses MacOS machine for development. To successfully install MongoDB, you can either install it by using homebrew on Mac or by downloading it from the MongoDB website.

    在本地系统上安装MongoDB。 请按照此处的说明下载并安装该版本,以供您选择操作系统。 本教程使用MacOS机器进行开发。 要成功安装MongoDB,您可以通过在Mac上使用自制软件来安装它,也可以从MongoDB网站上下载它。

  • A text editor installed, such as Visual Studio Code, Atom, or Sublime Text

    已安装的文本编辑器,例如Visual Studio Code,Atom或Sublime Text

为什么选择Nest和Vue ( Why Nest and Vue )

Nest.js has a reputation of bringing design patterns and mature structure to node.js world. This makes it quite easy to use it as the right tool for building awesome web applications. For the fact that Nest.js uses express library under the hood.

Nest.js在将设计模式和成熟的结构带入node.js世界方面享有盛誉。 这样就很容易将其用作构建出色的Web应用程序的正确工具。 因为Nest.js在后台使用了Express库。

Nest.js is fully featured and structured to support MVC (Model-View-Controller) design pattern.

Nest.js具有完整的功能和结构,可支持MVC(模型-视图-控制器)设计模式。

This means, you can install one of the popular template engine used in node.js and configure it to handle the flow of the application and interaction with backend API from the front end.

这意味着,您可以安装node.js中使用的一种流行的模板引擎,并将其配置为从前端处理应用程序流程以及与后端API的交互。

While this might be sufficient for a small app, it is always better to consider a much better and contemporary approach to handling frontend related part of an application by leveraging on a tool like Vue.js. Vue can be used to set up the frontend logic of your application as you will see later in this post.

尽管这对于一个小型应用程序可能已经足够了,但最好总是利用更好的现代方法来利用Vue.js之类的工具来处理应用程序的前端相关部分。 Vue可用于设置应用程序的前端逻辑,正如您将在本文后面看到的那样。

Vue.js is a progressive javaScript framework for building reusable components for user interfaces. It is simple and yet very powerful and ideal for any project. This makes it seamless to use for a Nest.js application for example.

Vue.js是一个渐进的javaScript框架,用于为用户界面构建可重用的组件。 它简单但功能强大,非常适合任何项目。 例如,这可以无缝地用于Nest.js应用程序。

As you proceed in this tutorial, you will learn how to use and successfully combine these two tools, that is, Nest.js and Vue.js to build highly interactive web app.

在学习本教程的过程中,您将学习如何使用并成功结合使用Nest.js和Vue.js这两个工具来构建高度交互的Web应用程序。

你会建立什么 ( What you’ll build )

As mentioned earlier in this post, you will build a customer list management application. To keep things really simple here, we will not be implementing authentication and authorization for any user. The main objective is for you to get conversant and comfortable using both Nest.js and Vue.js. At the end of the day, you would have learnt means to craft and structure this application as shown below:

如本文前面所述,您将构建一个客户列表管理应用程序。 为了使事情变得简单,我们不会为任何用户实施身份验证和授权。 主要目标是让您同时使用Nest.js和Vue.js并感到舒适。 最终,您将学到以下方法来制作和构造此应用程序:

We will use Nest.js to develop the backend API and then a Vue.js application to build components for creating, editing, deleting and showing the total list of customers from a mongoDB database.

我们将使用Nest.js开发后端API,然后使用Vue.js应用程序构建用于创建,编辑,删除和显示mongoDB数据库中的客户总数列表的组件。

安装Nest.js及其依赖项 ( Installing Nest.js and its dependencies )

Now that the basic introductory contents have been properly covered, you will proceed to installing Nest.js and its required dependencies. Getting Nest.js installed can be done in two different ways:

现在已经基本介绍了基本介绍内容,您将继续安装Nest.js及其必需的依赖项。 可以通过两种不同的方式来安装Nest.js:

  • Scaffold the base project with Nest CLI tool

    使用Nest CLI工具搭建基础项目

  • Installing the starter project on GitHub by using Git

    使用Git在GitHub上安装入门项目

You will use the Nest CLI here in this tutorial to easily craft a new Nest.js project. This comes with a lot of benefits like scaffolding a new project seamlessly, generating different files by using the nest command amongst other things.

您将在本教程的此处使用Nest CLI轻松制作新的Nest.js项目。 这带来了许多好处,例如无缝搭建新项目,通过使用nest命令等生成不同的文件。

First, you need to install the CLI globally on your system. To do that, run the following command from the terminal:

首先,您需要在系统上全局安装CLI。 为此,请从终端运行以下命令:

npm install -g @nestjs/cli

The installation of this tool will give you access to the nest command to manage the project and create Nest.js application related files as you will see later in this post.

安装此工具将使您可以访问nest命令来管理项目并创建Nest.js应用程序相关的文件,如您在本文后面会看到的那样。

Now that you have the CLI installed, you can now proceed to create the project for this tutorial by running the following command from your local development folder:

现在已经安装了CLI,现在可以通过在本地开发文件夹中运行以下命令来继续创建本教程的项目:

nest new customer-list-app-backend

The preceding command will generate a customer-list-app-backend application. Next, change directory into this new project and install the only server dependency for the backend. As mentioned earlier, you will use MongoDB database to persist data for the application. To integrate it with a Nest.js project, you need to install mongoose and the mongoose package built by the Nest.js team. Use the following command for this purpose:

前面的命令将生成一个customer-list-app-backend应用程序。 接下来,将目录更改为这个新项目,并为后端安装唯一的服务器依赖项。 如前所述,您将使用MongoDB数据库为应用程序保留数据。 要将其与Nest.js项目集成,您需要安装mongoose和Nest.js团队构建的mongoose软件包。 为此,请使用以下命令:

cd customer-list-app-backend

 npm install --save @nestjs/mongoose mongoose

启动应用程序 ( Start the application )

With the installation process properly covered, you can now start the application with:

正确涵盖了安装过程后,您现在可以使用以下方法启动应用程序:

npm run start

This will start the application on the default port of 3000. Navigate to http://localhost:3000 from your favorite browser and you should have a page similar to this:

这将在默认端口3000上启动应用程序。 从您喜欢的浏览器导航到http:// localhost:3000 ,您应该有一个类似于以下的页面:

与MongoDB数据库连接 ( Connecting with MongoDB database )

It is assumed that by now, you have installed MongoDB on your machine as instructed at the beginning of this post. To start the database, open a new terminal so that the application keeps running and run sudo mongod . The preceding command will start the MongoDB service and simultaneously run the database in the background.

假定到现在为止,您已经按照本文开头的说明在计算机上安装了MongoDB。 要启动数据库,请打开一个新终端,以使应用程序继续运行并run sudo mongod 。 前面的命令将启动MongoDB服务,并在后台同时运行数据库。

Next, to quickly setup a connection to the database from your application, you will have to import the installed MongooseModule within the root ApplicationModule. To do this, use your preferred code editor to open the project and locate ./src/app.module.ts. Update the contents with the following:

接下来,要从您的应用程序快速建立与数据库的连接,您将必须在根ApplicationModule导入已安装的MongooseModule 。 为此,请使用您喜欢的代码编辑器打开项目并找到./src/app.module.ts 。 使用以下内容更新内容:

// ./src/app.module.ts

import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { MongooseModule } from '@nestjs/mongoose';
@Module({
  imports: [
    MongooseModule.forRoot('mongodb://localhost/customer-app', { useNewUrlParser: true })
  ],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}

Here, Mongoose module for MongoDB uses the forRoot() method to supply the connection to the database.

在这里,用于MongoDB的Mongoose模块使用forRoot()方法来提供与数据库的连接。

设置和配置数据库架构,接口和DTO (Setting up and configuring a database schema, interfaces and DTO)

To properly structure the kind of data that will be stored in the database for this application, you will create a database schema, a TypeScript and a data transfer object (DTO).

为了正确构造将为此应用程序存储在数据库中的数据类型,您将创建一个数据库模式,一个TypeScript和一个数据传输对象(DTO)。

数据库架构 (Database schema)

Here, you will create a mongoose database schema that will determine the data that should be stored in the database. To begin, navigate to the ./src/ folder and first, create a new folder named customer and within the newly created folder, create another one and call it schemas. Now create a new file within the schemas and named customer.schema.ts . Open this newly created file and paste the following code in it:

在这里,您将创建一个猫鼬数据库架构,该架构将确定应存储在数据库中的数据。 首先,导航到./src/文件夹,然后首先创建一个名为customer的新文件夹,并在新创建的文件夹内创建另一个文件夹,并将其称为schemas 。 现在,在架构中创建一个新文件,并将其命名为customer.schema.ts 。 打开此新创建的文件,并将以下代码粘贴到其中:

// ./src/customer/schemas/customer.schema.ts
import * as mongoose from 'mongoose';

export const CustomerSchema = new mongoose.Schema({
    first_name: String,
    last_name: String,
    email: String,
    phone: String,
    address: String,
    description: String,
    created_at: { type: Date, default: Date.now }
})

This will ensure that data with string value will be stored in the database.

这将确保具有字符串值的数据将存储在数据库中。

介面 (Interfaces)

Next, you will create a TypeScript interface which will be used for type-checking and to determine the type of values that will be received by the application. To set it up, create a new folder named interfaces within the ./src/customer folder. After that, create a new file within the newly created folder and name it customer.interface.ts . Paste the following code in it :

接下来,您将创建一个TypeScript接口,该接口将用于类型检查并确定应用程序将接收的值的类型。 要进行设置,请在./src/customer文件夹中创建一个名为interfaces的新文件夹。 之后,在新创建的文件夹中创建一个新文件,并将其命名为customer.interface.ts 。 将以下代码粘贴到其中:

// ./src/customer/interfaces/customer.interface.ts
import { Document } from 'mongoose';

export interface Customer extends Document {
    readonly first_name: string;
    readonly last_name: string;
    readonly email: string;
    readonly phone: string;
    readonly address: string;
    readonly description: string;
    readonly created_at: Date;
}

数据传输对象(DTO) (Data transfer object (DTO))

A data transfer object will define how data will be sent on over the network. To do this, create a folder dto inside ./src/customer folder and create a new file create-customer.dto.ts and paste the code below in it:

数据传输对象将定义如何通过网络发送数据。 为此,请在./src/customer文件夹中创建一个文件夹dto并创建一个新文件create-customer.dto.ts并将以下代码粘贴到其中:

// ./src/customer/dto/create-customer.dto.ts
export class CreateCustomerDTO {
    readonly first_name: string;
    readonly last_name: string;
    readonly email: string;
    readonly phone: string;
    readonly address: string;
    readonly description: string;
    readonly created_at: Date;
}

You are now done with the basic configurations of connecting and interacting with the database

现在,您已经完成了与数据库连接和交互的基本配置

为应用程序创建模块,控制器和服务 ( Creating module, controller and service for the application )

生成模块 (Generate modules)

A module in Nest.js is identified by the @Module() decorator and it takes in objects such as controllers and providers. Here you will leverage on the nest command to easily generate a new module for the customer app. This will ensure that the application is properly structured and more organized. Stop the application, if it is currently running with CTRL + C and run the following command

Nest.js中的模块由@Module()装饰器标识,并且它接受诸如controllersproviders对象。 在这里,您将利用nest命令轻松为客户应用程序生成一个新模块。 这将确保应用程序具有正确的结构和更合理的组织。 停止应用程序(如果当前正在使用CTRL + C运行)并运行以下命令

nest generate module customer

This will create a new file named customer.module.ts within the src/customer folder and update the root module (i.e app.module.ts) of the application with the details of the newly created module.

这将在src/customer文件夹中创建一个名为customer.module.ts的新文件,并使用新创建的模块的详细信息更新应用程序的根模块(即app.module.ts )。

// ./src/customer/customer.module.ts

import { Module } from '@nestjs/common';
@Module({})
export class CustomerModule {}

You will come back to add more content to this module later in this post.

在本文的后面,您将回来向该模块添加更多内容。

产生服务 (Generate service)

Service also known as provider in Nest.js basically carry out the task of abstracting logic away from controllers. With it in place, a controller will only carry out the functionality of handling HTTP requests from the frontend and delegate the complex tasks to services. Service or provider in Nest.js is identified by adding @Injectable() decorator on top of them.

Nest.js中也称为提供程序的服务基本上是执行从控制器抽象逻辑的任务。 有了它,控制器将只执行处理来自前端的HTTP请求的功能,并将复杂的任务委托给服务。 Nest.js中的服务或提供者是通过在其顶部添加@Injectable()装饰器来标识的。

Generate a new service using the nest command by running the following command from the terminal within the project directory:

通过在项目目录中的终端上运行以下命令,使用nest命令生成新服务:

nest generateservice customer

After successfully running the command above, two new files will be created. They are:

成功运行以上命令后,将创建两个新文件。 他们是:

  • customer.service.ts: this is the main service file with @Injectable() decorator

    customer.service.ts :这是带有@Injectable()装饰器的主要服务文件

  • customer.service.spec.ts: a file for unit testing. You can ignore this file for now as testing will not be covered in this tutorial.

    customer.service.spec.ts :用于单元测试的文件。 您现在可以忽略此文件,因为本教程将不涉及测试。

The customer.service.ts file holds all the logic as regards database interaction for creating and updating every details of a new customer. In a nutshell, the service will receive a request from the controller, communicate this to the database and return an appropriate response afterwards.

customer.service.ts文件包含有关数据库交互的所有逻辑,这些逻辑用于创建和更新新客户的每个详细信息。 简而言之,服务将接收来自控制器的请求,将此请求传达给数据库,然后返回适当的响应。

Open the newly created customer.service.ts file and replace the existing code with the following :

打开新创建的customer.service.ts文件,并将现有代码替换为以下内容:

// ./src/customer/customer.service.ts
import { Injectable } from '@nestjs/common';
import { Model } from 'mongoose';
import { InjectModel } from '@nestjs/mongoose';
import { Customer } from './interfaces/customer.interface';
import { CreateCustomerDTO } from './dto/create-customer.dto';

@Injectable()
export class CustomerService {
    constructor(@InjectModel('Customer') private readonly customerModel: Model<Customer>) { }
    // fetch all customers
    async getAllCustomer(): Promise<Customer[]> {
        const customers = await this.customerModel.find().exec();
        return customers;
    }
    // Get a single customer
    async getCustomer(customerID): Promise<Customer> {
        const customer = await this.customerModel.findById(customerID).exec();
        return customer;
    }
    // post a single customer
    async addCustomer(createCustomerDTO: CreateCustomerDTO): Promise<Customer> {
        const newCustomer = await this.customerModel(createCustomerDTO);
        return newCustomer.save();
    }
    // Edit customer details
    async updateCustomer(customerID, createCustomerDTO: CreateCustomerDTO): Promise<Customer> {
        const updatedCustomer = await this.customerModel
            .findByIdAndUpdate(customerID, createCustomerDTO, { new: true });
        return updatedCustomer;
    }
    // Delete a customer
    async deleteCustomer(customerID): Promise<any> {
        const deletedCustomer = await this.customerModel.findByIdAndRemove(customerID);
        return deletedCustomer;
    }
}

Here, you imported the required module from @nestjs/common, mongoose and @nestjs/mongoose. In addition, you also imported the interface created earlier named Customer and a data transfer object CreateCustomerDTO.

在这里,你从进口所需模块@nestjs/commonmongoose@nestjs/mongoose 。 此外,您还导入了先前创建的名为Customer的接口和数据传输对象CreateCustomerDTO

In order to be able to seamlessly carry out several database related activities such as, creating a customer, retrieving the list of customers or just a single customer, you used the @InjectModel method to inject the Customer model into the CustomerService class.

为了能够无缝地执行几个与数据库有关的活动,例如,创建客户,检索客户列表或仅一个客户,您使用@InjectModel方法将Customer模型注入到CustomerService类中。

Next, you created the following methods:

接下来,您创建了以下方法:

  • getAllCustomer() : to retrieve and return the list of customers from the database

    getAllCustomer() :从数据库中检索并返回客户列表

  • getCustomer(): it takes customerID as a parameter and based on that, it will search and return the details of a user identified by that ID.

    getCustomer() :它将customerID作为参数,并基于该参数来搜索并返回由该ID标识的用户的详细信息。

  • addCustomer(): used to add a new customer to the database

    addCustomer() :用于向数据库添加新客户

  • updateCustomer(): this method also takes the ID of a customer as an argument and will be used to edit and update the details of such customer in the database.

    updateCustomer() :此方法还将客户的ID作为参数,将用于编辑和更新数据库中此类客户的详细信息。

  • deleteCustomer(): this will be used to delete the details of a particular customer completely from the database.

    deleteCustomer():这将用于从数据库中完全删除特定客户的详细信息。

生成控制器 (Generate controller)

Handling each route within the application is one of the major responsibility of controllers in Nest.js. Similar to most JavaScript server-side framework for the web, several endpoints will be created and any requests sent to such endpoint from the client side will be mapped to a specific method within the controller and an appropriate response will be returned.

在应用程序中处理每条路由是Nest.js中controllers的主要职责之一。 类似于大多数用于WebJavaScript服务器端框架,将创建多个端点,并且将从客户端发送到该端点的任何请求都映射到控制器内的特定方法,并返回适当的响应。

Again, you will use the nest command to generate the controller for this application. To achieve that, run the following command:

同样,您将使用nest命令来为此应用程序生成控制器。 为此,请运行以下命令:

nest generate controller customer

This command will also generate two new files within the src/customer, they are , customer.controller.spec.ts and customer.controller.ts files respectively. The customer.controller.ts file is the actual controller file and the second one should be ignored for now. Controllers in Nest.js are TypeScript files decorated with @Controller metadata.

此命令还将在src/customer生成两个新文件,分别是customer.controller.spec.tscustomer.controller.ts文件。 customer.controller.ts文件是实际的控制器文件,现在应该忽略第二个文件。 Nest.js中的控制器是装饰有@Controller元数据的TypeScript文件。

Now open the controller and replace the content with the following code that contains methods to create a new customer, retrieve the details of a particular customer and fetch the list of all customers from the database:

现在打开控制器,并用以下代码替换内容,该代码包含创建新客户,检索特定客户的详细信息并从数据库中获取所有客户列表的方法:

// ./src/customer/customer.controller.ts
import { Controller, Get, Res, HttpStatus, Post, Body, Put, Query, NotFoundException, Delete, Param } from '@nestjs/common';
import { CustomerService } from './customer.service';
import { CreateCustomerDTO } from './dto/create-customer.dto';

@Controller('customer')
export class CustomerController {
    constructor(private customerService: CustomerService) { }

    // add a customer
    @Post('/create')
    async addCustomer(@Res() res, @Body() createCustomerDTO: CreateCustomerDTO) {
        const customer = await this.customerService.addCustomer(createCustomerDTO);
        return res.status(HttpStatus.OK).json({
            message: "Customer has been created successfully",
            customer
        })
    }

    // Retrieve customers list
    @Get('customers')
    async getAllCustomer(@Res() res) {
        const customers = await this.customerService.getAllCustomer();
        return res.status(HttpStatus.OK).json(customers);
    }

    // Fetch a particular customer using ID
    @Get('customer/:customerID')
    async getCustomer(@Res() res, @Param('customerID') customerID) {
        const customer = await this.customerService.getCustomer(customerID);
        if (!customer) throw new NotFoundException('Customer does not exist!');
        return res.status(HttpStatus.OK).json(customer);
    }
}

In order to interact with the database, the CustomerService was injected into the controller via the class constructor(). The addCustomer() and getAllCustomer() methods will be used to add a new customer’s details and retrieve the list of customers while the getCustomer() receives the customerID as a query parameter and throw an exception if the customer does not exist in the database.

为了与数据库进行交互,将CustomerService通过类constructor()注入到控制器中。 addCustomer()getAllCustomer()方法将用于添加新客户的详细信息并检索客户列表,而getCustomer()则将customerID作为查询参数,如果客户不存在于数据库中,则抛出异常。

Next, you need to be able to update and delete the details of a customer where and when necessary. For this you will add two more methods to the CustomerController class. Open the file again and add this:

接下来,您需要能够在必要的位置和时间更新和删除客户的详细信息。 为此,您将向CustomerController类添加另外两个方法。 再次打开文件并添加以下内容:

// ./src/customer/customer.controller.ts
...
@Controller('customer')
export class CustomerController {
    constructor(private customerService: CustomerService) { }
    ...

    // Update a customer's details
    @Put('/update')
    async updateCustomer(@Res() res, @Query('customerID') customerID, @Body() createCustomerDTO: CreateCustomerDTO) {
        const customer = await this.customerService.updateCustomer(customerID, createCustomerDTO);
        if (!customer) throw new NotFoundException('Customer does not exist!');
        return res.status(HttpStatus.OK).json({
            message: 'Customer has been successfully updated',
            customer
        });
    }

    // Delete a customer
    @Delete('/delete')
    async deleteCustomer(@Res() res, @Query('customerID') customerID) {
        const customer = await this.customerService.deleteCustomer(customerID);
        if (!customer) throw new NotFoundException('Customer does not exist');
        return res.status(HttpStatus.OK).json({
            message: 'Customer has been deleted',
            customer
        })
    }
}

更新客户模块 ( Update the customer module )

To keep things properly organised go back to the customer.module.ts and set up the Customer model. Update the content with the following:

为了使事情井井有条,请回到customer.module.ts并设置Customer模型。 使用以下内容更新内容:

// ./src/customer/customer.module.ts

import { Module } from '@nestjs/common';
import { CustomerController } from './customer.controller';
import { CustomerService } from './customer.service';
import { MongooseModule } from '@nestjs/mongoose';
import { CustomerSchema } from './schemas/customer.schema';
@Module({
  imports: [
    MongooseModule.forFeature([{ name: 'Customer', schema: CustomerSchema }])
  ],
  controllers: [CustomerController],
  providers: [CustomerService]
})
export class CustomerModule { }

启用CORS ( Enable CORS )

By default, it is forbidden for two separate application on different ports to interact or share resources with each other unless it is otherwise allowed by one of them, which is often the server-side. In order to allow request from the client side that will be built with Vue.js, you will need to enable CORS (Cross-Origin Resource Sharing).

默认情况下,除非端口之一(通常是服务器端)另行允许,否则禁止在不同端口上的两个单独的应用程序相互交互或共享资源。 为了允许使用Vue.js构建的客户端请求,您需要启用CORS(跨源资源共享)。

To do that in Nest.js, you only need to add app.enableCors() to the main.ts file as shown below:

要在Nest.js中做到这一点,只需将app.enableCors()添加到main.ts文件中,如下所示:

// ./src/main.ts

import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  app.enableCors(); // add this line
  await app.listen(3000);
}
bootstrap();

With this, you have just completed the backend part of the application and can now proceed to build the frontend with Vue.js

这样,您刚刚完成了应用程序的后端部分,现在可以继续使用Vue.js构建前端

使用Vue.js创建前端应用 ( Creating the frontend app with Vue.js )

The team at Vue.js already created an awesome tool named Vue CLI. It is a standard tool that allows you to quickly generate and install a new Vue.js project with ease. You will use that here to create the frontend part of the customer app, but first you need to install Vue CLI globally on your machine.

Vue.js的团队已经创建了一个名为Vue CLI的出色工具。 它是一个标准工具,可让您轻松快速地生成并安装新的Vue.js项目。 您将在此处使用它来创建客户应用程序的前端部分,但是首先需要在计算机上全局安装Vue CLI。

Open a new terminal and run:

打开一个新终端并运行:

npm install -g @vue/cli

Once the installation process is complete, you can now use the vue create command to craft a new Vue.js project. Run the following command to do that for this project:

安装过程完成后,您现在可以使用vue create命令来制作新的Vue.js项目。 运行以下命令为此项目执行此操作:

vue create customer-list-app-frontend

Immediately after you hit return, you will be prompted to pick a preset. You can choose manually select features:

按下回车键后,将立即提示您选择一个预设。 您可以选择手动选择功能:

Next, check the features you will need for this project by using the up and down arrow key on your keyboard to navigate through the list of features. Press the spacebar to select a feature from the list. Select Babel, Router and Linter / Formatter as shown here:

接下来,使用键盘上的向上和向下箭头键在功能列表中导航,以检查该项目所需的功能。 按空格键从列表中选择一个功能。 选择Babel,路由器和Linter /格式化程序,如下所示:

Hitting return here will show you another list of options For other instructions, type y to use history mode for a router, this will ensure that history mode is enabled within the router file that will automatically be generated for this project.

点击此处返回将显示另一个选项列表。对于其他说明,请键入y以使用路由器的历史记录模式,这将确保在路由器文件中启用了历史记录模式,该文件将自动为此项目生成。

Next, select ESLint with error prevention only in order to pick a linter / formatter config. After that, select Lint on save for additional lint features and save your configuration in a dedicated config file for future projects. Type a name for your preset, I named mine vuescotch:

接下来, ESLint with error prevention only选择ESLint with error prevention only以选择一个linter / formatter配置。 之后,选择Lint on save获取其他棉绒功能,并将配置保存在专用的配置文件中以供将来的项目使用。 为您的预设键入一个名称,我命名为mine vuescotch

This will create a Vue.js application in a directory named customer-list-app-frontend and install all its required dependencies.

这将在名为customer-list-app-frontend的目录中创建Vue.js应用程序,并安装其所有必需的依赖项。

运行Vue.js应用 ( Run the Vue.js app )

You can now change directory into the newly created project and start the application with:

现在,您可以将目录更改为新创建的项目,并使用以下命令启动应用程序:

// change directorycd customer-list-app-frontend

// run the application
npm run serve

You can now view the application on http://localhost:8080

您现在可以在http:// localhost:8080上查看该应用程序

安装axios ( Install axios )

Axios, a promised based HTTP client for browser will be used here to perform HTTP requests from different component within the application. Stop the frontend application from running by hitting CTRL + C from the terminal and run the following command afterwards:

Axios,一个基于浏览器的基于HTTP的承诺客户端,将在这里用于执行来自应用程序内不同组件的HTTP请求。 通过在终端上按CTRL + C来停止前端应用程序的运行,然后再运行以下命令:

npm install axios --save

Once the installation process is completed, open the customer-list-app-frontend within a code editor and create a new file named helper.js within the src folder. Open the newly created file and paste the following content in it:

安装过程完成后,在代码编辑器中打开customer-list-app-frontend ,并在src文件夹中创建一个名为helper.js的新文件。 打开新创建的文件,并将以下内容粘贴到其中:

// ./src/helper.js
export const server = {
    baseURL: 'http://localhost:3000'
}

What you have done here is to define the `baseURL` for the backend project built with Nest.js. This is just to ensure that you don’t have to start declaring this URL within several Vue.js components that you will create in the next section.

您在这里所做的是为使用Nest.js构建的后端项目定义`baseURL`。 这只是为了确保您不必在下一节将要创建的多个Vue.js组件中开始声明此URL。

创建可重复使用的组件 ( Create reusable components )

Vue.js favours building and structuring applications by creating reusable components to give it a proper structure. Vue.js components contains three different sections, which are

Vue.js通过创建可重用的组件以提供适当的结构来支持构建和结构化应用程序。 Vue.js组件包含三个不同的部分,分别是

  • <template></template>

    <template></template>

  • <script></script>

    <script></script>

  • <style></style>.

    <style></style>

创建客户组件 ( Create customer component )

You will start by creating a component within the application for a user to create a customer. This component will contain a form with few input fields required to accepts details of a customer and once the form is submitted, the details from the input fields will be posted to the server. To achieve this, create a new folder named customer within the ./src/components folder. This newly created folder will house all the components for this application. Next, create another file within the customer folder and name it Create.vue. Open this new file and add the following:

您将首先在应用程序中创建组件以供用户创建客户。 该组件将包含一个表单,该表单具有用于接受客户详细信息所需的少量输入字段,并且一旦提交表单,来自输入字段的详细信息将被发布到服务器。 为此,请在./src/components文件夹中创建一个名为customer的新文件夹。 这个新创建的文件夹将容纳此应用程序的所有组件。 接下来,在customer文件夹中创建另一个文件,并将其命名为Create.vue 。 打开此新文件并添加以下内容:

// ./src/components/customer/Create.vue

<template>
   <div>
        <div class="col-md-12 form-wrapper">
          <h2> Create Customer </h2>
          <form id="create-post-form" @submit.prevent="createCustomer">
               <div class="form-group col-md-12">
                <label for="title"> First Name </label>
                <input type="text" id="first_name" v-model="first_name" name="title" class="form-control" placeholder="Enter firstname">
               </div>
               <div class="form-group col-md-12">
                <label for="title"> Last Name </label>
                <input type="text" id="last_name" v-model="last_name" name="title" class="form-control" placeholder="Enter Last name">
               </div>
             <div class="form-group col-md-12">
                <label for="title"> Email </label>
                <input type="text" id="email" v-model="email" name="title" class="form-control" placeholder="Enter email">
            </div>
            <div class="form-group col-md-12">
                <label for="title"> Phone </label>
                <input type="text" id="phone_number" v-model="phone" name="title" class="form-control" placeholder="Enter Phone number">
            </div>
            <div class="form-group col-md-12">
                <label for="title"> Address </label>
                <input type="text" id="address" v-model="address" name="title" class="form-control" placeholder="Enter Address">
            </div>
              <div class="form-group col-md-12">
                  <label for="description"> Description </label>
                  <input type="text" id="description" v-model="description" name="description" class="form-control" placeholder="Enter Description">
              </div>
              <div class="form-group col-md-4 pull-right">
                  <button class="btn btn-success" type="submit"> Create Customer </button>
              </div>           </form>
        </div>
    </div>
</template>

This is the <template></template> section that contains the details of the input fields. Next, paste the following code just after the end of the </template> tag:

这是<template></template>部分,其中包含输入字段的详细信息。 接下来,将以下代码粘贴在</template>标记的末尾:

// ./src/components/customer/Create.vue

...

<script>
import axios from "axios";
import { server } from "../../helper";
import router from "../../router";
export default {
  data() {
    return {
      first_name: "",
      last_name: "",
      email: "",
      phone: "",
      address: "",
      description: ""
    };
  },
  methods: {
    createCustomer() {
      let customerData = {
        first_name: this.first_name,
        last_name: this.last_name,
        email: this.email,
        phone: this.phone,
        address: this.address,
        description: this.description
      };
      this.__submitToServer(customerData);
    },
    __submitToServer(data) {
      axios.post(`${server.baseURL}/customer/create`, data).then(data => {
        router.push({ name: "home" });
      });
    }
  }
};
</script>

Here, you created a method createCustomer() to receive the details of a customer via the input fields and used axios to post the data to the server.

在这里,您创建了一个createCustomer()方法来通过输入字段接收客户的详细信息,并使用axios将数据发布到服务器。

编辑组件 ( Edit component )

Similar to the CreateCustomer component, you need to create another component to edit customer’s details. Navigate to ./src/components/customer and create a new file named Edit.vue. Paste the following code in it:

CreateCustomer组件类似,您需要创建另一个组件来编辑客户的详细信息。 导航到./src/components/customer并创建一个名为Edit.vue的新文件。 在其中粘贴以下代码:

// ./src/components/customer/Edit.vue

<template>
   <div>
        <h4 class="text-center mt-20">
         <small>
         <button class="btn btn-success" v-on:click="navigate()"> View All Customers </button>
         </small>
        </h4>
        <div class="col-md-12 form-wrapper">
          <h2> Edit Customer </h2>
          <form id="create-post-form" @submit.prevent="editCustomer">
               <div class="form-group col-md-12">
                <label for="title"> First Name </label>
                <input type="text" id="first_name" v-model="customer.first_name" name="title" class="form-control" placeholder="Enter firstname">
               </div>
               <div class="form-group col-md-12">
                <label for="title"> Last Name </label>
                <input type="text" id="last_name" v-model="customer.last_name" name="title" class="form-control" placeholder="Enter Last name">
               </div>
             <div class="form-group col-md-12">
                <label for="title"> Email </label>
                <input type="text" id="email" v-model="customer.email" name="title" class="form-control" placeholder="Enter email">
            </div>
            <div class="form-group col-md-12">
                <label for="title"> Phone </label>
                <input type="text" id="phone_number" v-model="customer.phone" name="title" class="form-control" placeholder="Enter Phone number">
            </div>
            <div class="form-group col-md-12">
                <label for="title"> Address </label>
                <input type="text" id="address" v-model="customer.address" name="title" class="form-control" placeholder="Enter Address">
            </div>
              <div class="form-group col-md-12">
                  <label for="description"> Description </label>
                  <input type="text" id="description" v-model="customer.description" name="description" class="form-control" placeholder="Enter Description">
              </div>
              <div class="form-group col-md-4 pull-right">
                  <button class="btn btn-success" type="submit"> Edit Customer </button>
              </div>           </form>
        </div>
    </div>
</template>
<script>
import { server } from "../../helper";
import axios from "axios";
import router from "../../router";
export default {
  data() {
    return {
      id: 0,
      customer: {}
    };
  },
  created() {
    this.id = this.$route.params.id;
    this.getCustomer();
  },
  methods: {
    editCustomer() {
      let customerData = {
        first_name: this.customer.first_name,
        last_name: this.customer.last_name,
        email: this.customer.email,
        phone: this.customer.phone,
        address: this.customer.address,
        description: this.customer.description
      };
      axios
        .put(
          `${server.baseURL}/customer/update?customerID=${this.id}`,
          customerData
        )
        .then(data => {
          router.push({ name: "home" });
        });
    },
    getCustomer() {
      axios
        .get(`${server.baseURL}/customer/customer/${this.id}`)
        .then(data => (this.customer = data.data));
    },
    navigate() {
      router.go(-1);
    }
  }
};
</script>

The route parameter was used here to fetch the details of a customer from the database and populated the inputs fields with it. As a user of the application, you can now edit the details and submit back to the server.

在这里,使用route参数从数据库中获取客户的详细信息,并在其中填充输入字段。 作为该应用程序的用户,您现在可以编辑详细信息并提交回服务器。

The editCustomer() method within the <script></script> was used to send a PUT HTTP request to the server.

<script></script>中的editCustomer()方法用于将PUT HTTP请求发送到服务器。

查看所有客户 ( View all customer )

Finally, to fetch and show the complete list of customers from the server, you will create a new component. Navigate to the views folder within the src folder, you should see a Home.vue file, if otherwise, create it and paste this code in it:

最后,要从服务器获取并显示完整的客户列表,您将创建一个新组件。 导航到src文件夹中的views文件夹,您应该看到一个Home.vue文件,否则,请创建该文件并将其粘贴在其中:

// ./src/views/Home.vue
<template>
    <div class="container-fluid">
      <div class="text-center">
        <h1>Nest Customer List App Tutorial</h1>
       <p> Built with Nest.js, Vue.js and MongoDB</p>
       <div v-if="customers.length === 0">
            <h2> No customer found at the moment </h2>
        </div>
      </div>

        <div class="">
            <table class="table table-bordered">
              <thead class="thead-dark">
                <tr>
                  <th scope="col">Firstname</th>
                  <th scope="col">Lastname</th>
                  <th scope="col">Email</th>
                  <th scope="col">Phone</th>
                  <th scope="col">Address</th>
                  <th scope="col">Description</th>
                  <th scope="col">Actions</th>
                </tr>
              </thead>
              <tbody>
                <tr v-for="customer in customers" :key="customer._id">
                  <td>{{ customer.first_name }}</td>
                  <td>{{ customer.last_name }}</td>
                  <td>{{ customer.email }}</td>
                  <td>{{ customer.phone }}</td>
                  <td>{{ customer.address }}</td>
                  <td>{{ customer.description }}</td>
                  <td>
                    <div class="d-flex justify-content-between align-items-center">
                                <div class="btn-group" style="margin-bottom: 20px;">
                                  <router-link :to="{name: 'Edit', params: {id: customer._id}}" class="btn btn-sm btn-outline-secondary">Edit Customer </router-link>
                                  <button class="btn btn-sm btn-outline-secondary" v-on:click="deleteCustomer(customer._id)">Delete Customer</button>
                                </div>
                              </div>
                  </td>
                </tr>
              </tbody>
            </table>
          </div>
    </div>
</template>
<script>
import { server } from "../helper";
import axios from "axios";
export default {
  data() {
    return {
      customers: []
    };
  },
  created() {
    this.fetchCustomers();
  },
  methods: {
    fetchCustomers() {
      axios
        .get(`${server.baseURL}/customer/customers`)
        .then(data => (this.customers = data.data));
    },
    deleteCustomer(id) {
      axios
        .delete(`${server.baseURL}/customer/delete?customerID=${id}`)
        .then(data => {
          console.log(data);
          window.location.reload();
        });
    }
  }
};
</script>

Within <template> section, you created an HTML table to display all customers details and used the <router-link> to create a link for editing and to a view a single customer by passing the customer._id as a query parameter. And finally, within the <script> section of this file, you created a method named fetchCustomers() to fetch all customers from the database and updated the page with the data returned from the server.

<template>部分中,您创建了一个HTML表来显示所有客户的详细信息,并使用<router-link>创建了一个编辑链接,并通过将customer._id作为查询参数来查看单个客户。 最后,在此文件的<script>部分中,您创建了一个名为fetchCustomers()的方法,用于从数据库中获取所有客户,并使用服务器返回的数据更新页面。

更新App.vue ( Update App.vue )

Open the AppComponent of the application and update it with the links to both Home and Create component by using the content below:

打开应用程序的AppComponent ,并使用以下内容使用指向HomeCreate组件的链接进行更新:

// ./src/App.vue

<template>
  <div id="app">
    <div id="nav">
      <router-link to="/">Home</router-link> |
      <router-link to="/create">Create</router-link>
    </div>
    <router-view/>
  </div>
</template>

<style>
...
.form-wrapper {
  width: 500px;
  margin: 0 auto;
}
</style>

Also included is a <style></style> section to include styling for the forms.

还包括一个<style></style>部分,以包括表单的样式。

包括引导程序 ( Include Bootstrap )

Navigate to the index.html file within the public folder and include the CDN file for bootstrap as shown below. This is just to give the page some default style:

导航到公用文件夹中的index.html文件,并包含用于引导的CDN文件,如下所示。 这只是为页面提供一些默认样式:

<!DOCTYPE html>
<html lang="en">
<head>
  ...
  <!-- Add this line -->
  <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css">
  <title>customer-list-app-frontend</title>
</head>
<body>
   ...
</body>
</html>

设置路由 ( Setting up routing )

Finally, configure the router file within ./src/router.js to include the link to all the required reusable components created so far by updating its content as shown here:

最后,在./src/router.js配置路由器文件,以包括到到目前为止通过更新其内容创建的所有必需的可重用组件的链接,如下所示:

// ./src/router.js

import Vue from 'vue'
import Router from 'vue-router'
import HomeComponent from '@/views/Home';
import EditComponent from '@/components/customer/Edit';
import CreateComponent from '@/components/customer/Create';
Vue.use(Router)
export default new Router({
  mode: 'history',
  routes: [
    { path: '/', redirect: { name: 'home' } },
    { path: '/home', name: 'home', component: HomeComponent },
    { path: '/create', name: 'Create', component: CreateComponent },
    { path: '/edit/:id', name: 'Edit', component: EditComponent },
  ]
});

测试应用 ( Test the application )

You can now proceed to test the application by running npm run serve to start it and navigate to http://localhost:8080 to view it:

现在,您可以通过运行npm run serve来启动它,并导航到http:// localhost:8080来对其进行测试:

Ensure that the backend server is running at this moment, if otherwise, navigate to the backend application from a different terminal and run:

确保此时正在运行后端服务器,否则,请从其他终端导航到后端应用程序并运行:

npm run start

Lastly, also ensure that the MongoDB instance is running as well. Use sudo mongod from another terminal on your local system, if it is not running at the moment.

最后,还要确保MongoDB实例也正在运行。 如果当前未在其他终端上使用sudo mongod ,请使用它。

建立新客户 ( Create new customer )

主页(查看所有客户) ( Homepage ( View all customers ) )

编辑客户详细信息 ( Edit a customer details )

结论 ( Conclusion )

In this tutorial, you have created a simple customer list management application by using Nest.js and Vue.js. Here, you used Nest.js to build a RESTful backend API and then leveraged on Vue.js to craft a client that consumes the API.

在本教程中,您已经使用Nest.js和Vue.js创建了一个简单的客户列表管理应用程序。 在这里,您使用Nest.js来构建RESTful后端API,然后利用Vue.js来制作使用该API的客户端。

This has given you an overview of how to structure Nest.js application and integrate it with a MongoDB database.

这概述了如何构造Nest.js应用程序并将其与MongoDB数据库集成。

I hope you found this tutorial helpful. Don’t hesitate to explore the source code of both application by checking it out here on GitHub.

希望本教程对您有所帮助。 不要犹豫,通过在GitHub上查看这两个应用程序的源代码。

翻译自: https://scotch.io/tutorials/building-a-modern-app-using-nestjs-mongodb-and-vuejs

nest.js

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值