Как создать блог с помощью Nest.js, MongoDB и Vue.js

внешний интерфейс Программа перевода самородков NestJS

Как создать блог с помощью Nest.js, MongoDB и Vue.js

Обзор

Nest.js— это расширяемый серверный JavaScript-фреймворк. Он построен с использованием TypeScript, поэтому он по-прежнему совместим с JavaScript, что делает его эффективным инструментом для создания эффективных и надежных серверных приложений. Он также имеет модульную структуру, которая обеспечивает зрелый структурированный шаблон проектирования для среды разработки Node.js.

Vue.js— это интерфейсный JavaScript-фреймворк для создания пользовательских интерфейсов. У него не только простой, но мощный API, но и отличная производительность. Vue.js может предоставить внешний слой и логику любого веб-приложения масштаба проекта. Он может легко интегрироваться с другими библиотеками или существующими проектами, что делает его идеальным для большинства современных веб-приложений.

В этом руководстве мы познакомимся с его строительными блоками и основными принципами создания современного веб-приложения путем создания приложения Nest.js. Мы разделим приложение на две отдельные части: интерфейс и серверную часть. Во-первых, мы будем использовать Nest.js для создания серверного API RESTful. Затем Vue.js будет использоваться для создания внешнего интерфейса. Внешние и внутренние приложения будут работать на разных портах и ​​будут работать как отдельные домены.

Мы создадим приложение для ведения блога, которое пользователи смогут использовать для создания и сохранения новых статей, просмотра сохраненных статей на домашней странице и других действий, таких как редактирование и удаление статей. Кроме того, мы подключаем приложение и сохраняем данные приложения вMongoDBMongoDB — это база данных NoSQL без схемы, которая может получать и хранить файлы JSON. Основное внимание в этом руководстве уделяется тому, как создать приложение в среде разработки. Если это в производственной среде, мы также должны учитывать аутентификацию пользователя приложения.

помещение

Для выполнения этого урока нам понадобится:

  • Установить локальноNode.js(по крайней мере версия v6) иnpm(по крайней мере версия v5.2). Node.js — это среда выполнения, которая позволяет запускать код JavaScript вне браузера. Он поставляется сnpmПредустановленный инструмент управления пакетами, позволяющий устанавливать и обновлять пакеты. Следуйте статье, если хотите установить их на macOS или Ubuntu 18.04.Как установить Node.js на macOS и создать локальную среду разработкишаги или статьи вКак установить Node.js на Ubuntu 18.04Раздел «Установка с помощью PPA» в .
  • Установите базу данных MongoDB на свой компьютер. согласно сздесьинструкции по загрузке и установке версии для вашей операционной системы. Вы можете сделать это,Macиспользовать наHomebrewдля установки или изВеб-сайт MongoDBскачать.
  • для TypeScript иJavaScriptИметь базовое понимание.
  • Установите текстовый редактор, напримерVisual Studio Code,AtomилиSublime Text.

Уведомление:В этом руководстве для разработки используется компьютер с macOS. Если вы используете другую операционную систему, вам может понадобиться использоватьsudoвыполнитьnpmЗаказ.

Шаг 1. Установите Nest.js и другие зависимости

В этом разделе мы сначала устанавливаем Nest.js и его необходимые зависимости локально. Вы можете использоватьCLIЛегко установите Nest.js или из стартового проекта на GitHub. В этом руководстве мы будем использовать CLI для инициализации приложения. Сначала выполните следующую команду в Терминале, чтобы установить ее глобально на свой компьютер:

npm i -g @nestjs/cli

Вы увидите вывод, подобный следующему:

Output@nestjs/cli@5.8.0
added 220 packages from 163 contributors in 49.104s

Чтобы подтвердить, что установка Nest CLI завершена, запустите эту команду на терминале:

nest --version

Вы увидите версию Nest, установленную на вашем компьютере:

Output5.8.0

мы будем использоватьnestкоманды для управления проектом и использования его для создания связанных файлов, таких как контроллеры, модули и поставщики.

Чтобы запустить проект для этого руководства, в терминале используйтеnestкоманда для запуска следующей командной строки для создания файла с именемblog-backendНовый проект Nest.js для:

nest new blog-backend

После запуска этой командыnestнемедленно предоставит вам некоторую основную информацию, такую ​​как描述(description),版本(version)а также作者(author). Идите вперед и предоставьте соответствующие детали. После того, как вы ответили на каждое приглашение, на вашем компьютере нажмите回车Продолжать.

Далее мы выберем менеджер пакетов. Для этого руководства выберитеnpmи нажмите回车键Начните установку Nest.js.

Alt 创建一个 Nest 项目

Это будет в локальной папке разработки по адресуblog-backendВ папке создается новый проект Nest.js.

Затем перейдите в новую папку проекта из терминала:

cd blog-backend

Выполните следующие команды, чтобы установить дополнительные зависимости службы:

npm install --save @nestjs/mongoose mongoose

На данный момент мы установили@nestjs/mongooseа такжеmongoose, первый представляет собой специфичный для Nest.js пакет инструментов объектного моделирования для MongoDB, а второй — пакет для управления Mongoose.

Теперь запустите приложение с помощью следующей команды:

npm run start

Теперь выберите предпочитаемый браузер, откройтеhttp://localhost:3000, вы увидите, что наше приложение работает.

Alt 新安装的 Nest.js 应用的欢迎页面

Теперь мы успешно создали проект с помощью команд Nest CLI. Затем продолжайте и запустите приложение с портом по умолчанию на локальном компьютере.3000чтобы получить к нему доступ. В следующем разделе мы более подробно рассмотрим приложение, настроив конфигурацию подключения к базе данных.

Шаг 2. Настройка и подключение к базе данных

На этом этапе мы настроим и интегрируем MongoDB в приложение Nest.js и будем использовать MongoDB для хранения данных приложения. MongoDB преобразует данные вполе: значениепары хранятся в видеdocumentсередина. вы будете использоватьMongooseЧтобы получить доступ к этим структурам данных, Mongoose представляет собой объектно-документное моделирование (ODM), которое позволяет нам определять структуры схемы, которые представляют типы данных, хранящихся в базе данных MongoDB.

Чтобы запустить MongoDB, сначала откройте отдельный терминал, чтобы приложение могло продолжать работать, а затем выполните следующую команду:

sudo mongod

Это запустит службу MongoDB и запустит базу данных в фоновом режиме на вашем компьютере.

Открыть в текстовом редактореblog-backendпроект, таргетинг./src/app.module.tsдокумент. Мы можем сделать это, укоренивApplicationModuleустановлен вMongooseModuleустановить соединение с базой данных. Необходимо добавить следующие строки кода для обновленияapp.module.tsСодержание в:

~/blog-backend/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/nest-blog', { useNewUrlParser: true }),
  ],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule { }

В этом файле мы используемforRoot()метод для завершения подключения к базе данных. Когда вы закончите редактирование, сохраните и закройте файл.

С их помощью мы можем использовать соответствующий модуль MongoDB в Mongoose для установления соединения с базой данных. В следующем разделе мы создадим схему базы данных, используя библиотеку Mongoose, интерфейс TypeScript и схему объекта передачи данных (DTO).

Шаг 3 — Создайте схему базы данных, интерфейс и DTO

На этом этапе мы будем использовать Mongoose для создания базы данных.schema,интерфейса такжеобъект передачи данных. Mongoose помогает нам управлять отношениями между данными и обеспечивает проверку схемы для типов данных. Чтобы лучше определить структуры данных и типы данных в базе данных в нашем приложении, мы создадим файлы для определения следующего:

  • схема базы данных: это организация данных, представляющая собой план, определяющий структуру и типы данных, которые должна хранить база данных.
  • интерфейс: Интерфейс TypeScript для проверки типов. Его можно использовать для определения типа данных, передаваемых в приложении.
  • объект передачи данных: этот объект определяет, как данные отправляются по сети и как они передаются между процессами.

Сначала вернитесь к терминалу, где запущено текущее приложение, и используйтеCTRL + Cостановить процесс, перейти к./src/папка:

cd ./src/

Затем создайте файл с именемblogкаталог и создатьschemasпапка:

mkdir -p blog/schemas

существуетschemasпапку, создайте файл с именемblog.schema.tsновый файл. Откройте его текстовым редактором. Затем добавьте следующее:

~/blog-backend/src/blog/schemas/blog.schema.ts

import * as mongoose from 'mongoose';

export const BlogSchema = new mongoose.Schema({
    title: String,
    description: String,
    body: String,
    author: String,
    date_posted: String
})

Здесь мы используем Mongoose для определения типа данных, которые будут храниться в базе данных. Мы указали, что все поля, которые будут сохранены и приняты, имеют только строковый тип. Сохраните и закройте файл, когда закончите редактирование.

Теперь, когда схема базы данных определена, пришло время перейти к созданию интерфейса.

Сначала вернитесь кblogпапка:

cd ~/blog-backend/src/blog/

Создатьinterfaces, и перейдите в папку:

mkdir interfaces

существуетinterfacesпапку, создайте файл с именемpost.interface.tsфайл и откройте его текстовым редактором. Добавьте следующее, чтобы определитьPostтип данных:

~/blog-backend/src/blog/interfaces/post.interface.ts

import { Document } from 'mongoose';

export interface Post extends Document {
    readonly title: string;
    readonly description: string;
    readonly body: string;
    readonly author: string;
    readonly date_posted: string
}

В этом файле мы успешно поместилиPostТип данных типа определяется как строковое значение. Сохраните и закройте файл.

Поскольку наше приложение будет отправлять данные в базу данных, мы создадим объект передачи данных, который будет определять, как данные будут отправляться в сеть.

Для этого, пожалуйста./src/blogСоздать папку в папкеdto. В только что созданной папке создайте файл с именемcreate-post.dto.tsдокумент

Таргетингblogпапка:

cd ~/blog-backend/src/blog/

Затем создайте файл с именемdtoпапку и перейти в эту папку:

mkdir dto

существуетdtoпапку, создайте файл с именемcreate-post.dtoновый файл. Откройте его текстовым редактором и добавьте следующее:

~/blog-backend/src/blog/dto/create-post.dto.ts

export class CreatePostDTO {
    readonly title: string;
    readonly description: string;
    readonly body: string;
    readonly author: string;
    readonly date_posted: string
}

мы поставилиCreatePostDTOКаждое свойство в классе помечено типом данныхstring, и отмечен какreadonly, чтобы избежать ненужных манипуляций с данными. Сохраните и закройте файл, когда закончите редактирование.

На этом шаге мы создали схему базы данных, интерфейс и объекты передачи данных для базы данных, в которой будут храниться данные. Далее мы создадим модули, контроллеры и сервисы для блога.

Шаг 4. Создайте модуль, контроллер и сервис для своего блога.

На этом этапе мы улучшим существующую структуру приложения, создав модуль для блога. Этот модуль организует файловую структуру в приложении. Далее мы создадим контроллер для обработки маршрутизации и HTTP-запросов от клиентов. Наконец, мы создадим сервисы для обработки сложной бизнес-логики, которую не могут обработать все контроллеры в приложении.

Создать модуль

Как и интерфейсные фреймворки, такие как Angular, Nest.js использует модульный синтаксис. Приложения Nest.js являются модульными, они поставляются с предустановленным одним корневым модулем, которого обычно достаточно для небольших приложений. Однако, когда бизнес приложений начинает расти, Nest.js рекомендует использовать несколько модулей для организации приложения, разбивая код на разные модули в соответствии со связанными функциями.

в Nest.jsмодульЗависит от@Module()идентификатор декоратора и принимаетcontrollerа такжеproviderобъекты со свойствами, такими как . Каждое из этих свойств принимает отдельный наборcontrollerа такжеprovider.

Мы создадим новый модуль для этого блог-приложения, чтобы сделать структуру более организованной. Во-первых, еще~/blog-backendпапке выполните следующую команду:

nest generate module blog

Вы увидите вывод, подобный следующему:

OutputCREATE /src/blog/blog.module.ts

UPDATE /src/app.module.ts

Эта команда создает файл с именемblog.module.tsновый модуль. Импортируйте вновь созданный модуль в корневой модуль приложения. Это позволит Nest.js знать о существовании другого модуля, кроме корневого модуля.

В этом файле вы увидите следующий код:

~/blog-backend/src/blog/blog.module.ts

import { Module } from '@nestjs/common';

@Module({})
export class BlogModule {}

Позже в этом руководстве мы добавим в него нужные свойства.BlogModule. Теперь сохраните и выйдите из файла.

Создать сервис

Служить(также называемый провайдером в Nest.js) предназначен для удаления бизнес-логики из контроллеров, которые должны обрабатывать только HTTP-запросы, и перенаправлять более сложные задачи другим классам обслуживания. Сервисы — это обычные классы JavaScript с над своим кодом@Injectable()декоратор. Чтобы создать новую службу, выполните следующую команду в терминале в каталоге проекта:

nest generate service blog

Вы увидите вывод, подобный следующему:

Output  CREATE /src/blog/blog.service.spec.ts (445 bytes)

CREATE /src/blog/blog.service.ts (88 bytes)

UPDATE /src/blog/blog.module.ts (529 bytes)

здесь черезnestкоманда создаетblog.service.spec.tsфайл, мы можем использовать его для тестирования. Он также создает новыйblog.service.tsфайл, который будет содержать всю логику для этого приложения и обрабатывать добавление и извлечение документов в базу данных MongoDB. Кроме того, он автоматически импортирует вновь созданные сервисы и добавляет их вblog.module.tsсередина.

Служба обрабатывает всю логику в приложении и отвечает за взаимодействие с базой данных и возврат соответствующего ответа контроллеру. Для этого откройте в текстовом редактореblog.service.tsфайл и замените содержимое следующим:

~/blog-backend/src/blog/blog.service.ts

import { Injectable } from '@nestjs/common';
import { Model } from 'mongoose';
import { InjectModel } from '@nestjs/mongoose';
import { Post } from './interfaces/post.interface';
import { CreatePostDTO } from './dto/create-post.dto';

@Injectable()
export class BlogService {

    constructor(@InjectModel('Post') private readonly postModel: Model<Post>) { }

    async getPosts(): Promise<Post[]> {
        const posts = await this.postModel.find().exec();
        return posts;
    }

    async getPost(postID): Promise<Post> {
        const post = await this.postModel
            .findById(postID)
            .exec();
        return post;
    }

    async addPost(createPostDTO: CreatePostDTO): Promise<Post> {
        const newPost = await this.postModel(createPostDTO);
        return newPost.save();
    }

    async editPost(postID, createPostDTO: CreatePostDTO): Promise<Post> {
        const editedPost = await this.postModel
            .findByIdAndUpdate(postID, createPostDTO, { new: true });
        return editedPost;
    }

    async deletePost(postID): Promise<any> {
        const deletedPost = await this.postModel
            .findByIdAndRemove(postID);
        return deletedPost;
    }

}

В этом файле мы сначала начинаем с@nestjs/common,mongooseа также@nestjs/mongooseимпортировать необходимые модули. В то же время мы также импортировалиPostинтерфейс и объект передачи данныхCreatePostDTO.

существуетconstructor, мы использовали@InjectModel('Post'),БудуPostмодель внедрить этоBlogServiceв классе. Теперь мы можем использовать эту введенную модель для извлечения всех статей, извлечения статьи и выполнения других действий, связанных с базой данных.

Далее мы создали следующий метод:

  • getPosts(): Получить все статьи из базы данных.
  • getPost(): извлекает статью из базы данных.
  • addPost(): добавить новую статью.
  • editPost(): обновить статью.
  • deletePost(): удалить определенную статью.

Когда закончите, сохраните и закройте файл.

Мы завершили настройку и создание нескольких методов, которые будут передавать соответствующее взаимодействие с базой данных MongoDB через backend API. Теперь мы создадим маршруты, необходимые для обработки HTTP-вызовов от внешнего клиента.

Создать контроллер

В Nest.js,контроллерОтвечает за обработку любых запросов от клиентов приложений и возврат соответствующих ответов. Как и в большинстве других веб-фреймворков, для приложения важно прослушивать запросы и отвечать на них.

Чтобы обслуживать все HTTP-запросы для приложения блога, мы будем использоватьnestкоманда для создания нового файла контроллера. Сначала убедитесь, что вы все еще находитесь в каталоге проекта,blog-backend, затем выполните следующие команды:

nest generate controller blog

Вы увидите вывод, подобный следующему:

OutputCREATE /src/blog/blog.controller.spec.ts (474 bytes)

CREATE /src/blog/blog.controller.ts (97 bytes)

UPDATE /src/blog/blog.module.ts (483 bytes)

Этот вывод указывает, что команда находится вsrc/blogВ каталоге создаются два новых файла,blog.controller.spec.tsа такжеblog.controller.ts. Первый — это файл, который можно использовать для написания автоматических тестов для вновь созданных контроллеров. Последний является самим файлом контроллера. Контроллеры в Nest.js используют@ControllerФайлы TypeScript, украшенные метаданными. Эта команда также импортирует только что созданный контроллер и добавляет его в модуль блога.

Далее открываем текстовым редакторомblog.controller.tsфайл и обновить его с помощью:

~/blog-backend/src/blog/blog.controller.ts

import { Controller, Get, Res, HttpStatus, Param, NotFoundException, Post, Body, Query, Put, Delete } from '@nestjs/common';
import { BlogService } from './blog.service';
import { CreatePostDTO } from './dto/create-post.dto';
import { ValidateObjectId } from '../shared/pipes/validate-object-id.pipes';


@Controller('blog')
export class BlogController {

    constructor(private blogService: BlogService) { }

    @Get('posts')
    async getPosts(@Res() res) {
        const posts = await this.blogService.getPosts();
        return res.status(HttpStatus.OK).json(posts);
    }

    @Get('post/:postID')
    async getPost(@Res() res, @Param('postID', new ValidateObjectId()) postID) {
        const post = await this.blogService.getPost(postID);
        if (!post) throw new NotFoundException('Post does not exist!');
        return res.status(HttpStatus.OK).json(post);

    }

    @Post('/post')
    async addPost(@Res() res, @Body() createPostDTO: CreatePostDTO) {
        const newPost = await this.blogService.addPost(createPostDTO);
        return res.status(HttpStatus.OK).json({
            message: "Post has been submitted successfully!",
            post: newPost
        })
    }
}

В этом файле мы сначала вводим@nestjs/commonМодули модуля, необходимые для обработки HTTP-запросов. Затем мы представили три новых модуля:BlogService,CreatePostDTOа такжеValidateObjectId. После этого, добавив в конструкторBlogServiceВнедрить в контроллер, чтобы иметь доступ к использованиюBlogServiceФункции, уже определенные в файле. В Nest.js это шаблон, который называетсявнедрение зависимости, что помогает повысить эффективность и улучшить модульность приложения.

Наконец, мы создаем эти асинхронные методы:

  • getPosts(): этот метод будет выполнять функцию извлечения всех статей из базы данных при получении HTTP-запроса GET от клиента, а затем возвращать соответствующий ответ. оно использует@Get('posts')Украсить.
  • getPost(): это начнется сpostIDВ качестве аргумента получить статью из базы данных. В дополнение к переданному этому методуpostIDВ дополнение к параметрам он также реализуетValidateObjectId()дополнительный метод. Этот метод реализует Nest.jsPipeTransformинтерфейс. Он используется для проверки и проверки того, что его можно найти в базе данных.postIDпараметр. Мы определим этот метод в следующем разделе.
  • addPost(): этот метод будет обрабатывать HTTP-запросы POST для добавления новых статей в базу данных.

Чтобы иметь возможность редактировать и удалять определенные статьи, нам нужноblog.controller.tsДобавьте в файл более двух методов. нам нужно, прежде чем добавить вblog.controller.tsизaddPost()После метода непосредственно добавьтеeditPost()а такжеdeletePost()метод:

~/blog-backend/src/blog/blog.controller.ts

...
@Controller('blog')
export class BlogController {
    ...
    @Put('/edit')
    async editPost(
        @Res() res,
        @Query('postID', new ValidateObjectId()) postID,
        @Body() createPostDTO: CreatePostDTO
    ) {
        const editedPost = await this.blogService.editPost(postID, createPostDTO);
        if (!editedPost) throw new NotFoundException('Post does not exist!');
        return res.status(HttpStatus.OK).json({
            message: 'Post has been successfully updated',
            post: editedPost
        })
    }


    @Delete('/delete')
    async deletePost(@Res() res, @Query('postID', new ValidateObjectId()) postID) {
        const deletedPost = await this.blogService.deletePost(postID);
        if (!deletedPost) throw new NotFoundException('Post does not exist!');
        return res.status(HttpStatus.OK).json({
            message: 'Post has been deleted!',
            post: deletedPost
        })
    }
}

Вот объяснение того, что именно мы добавили:

  • editPost(): этот метод принимаетpostIDПараметры запроса и выполните функцию обновления статьи. Это также используетValidateObjectIdМетоды предоставляют соответствующую сертификацию для статей, которые вам необходимо отредактировать.
  • deletePost(): этот метод принимаетpostIDпараметры запроса и удалить определенные статьи из базы данных.

а такжеBlogControllerТочно так же каждый определенный здесь асинхронный метод имеет декоратор метаданных и включает префикс, используемый в механизме маршрутизации в Nest.js. Он контролирует, какие запросы получает каждый контроллер, и указывает на методы ответа, которые должны обрабатывать запрос и возвращать его соответственно.

Например, мы создали в этом разделеBlogControllerимеютblogпрефикс и имяgetPosts()использоватьpostsПрефиксный метод. Это означает отправку вblog/posts(http:localhost:3000/blog/posts), любой запрос GET будетgetPosts()обработка методом. Другие методы обработки HTTP-запросов аналогичны приведенным в этом примере.

Сохраните и закройте файл.

Полное о приложенииblog.controller.tsдокументы, пожалуйста, посетитеDO Community repository.

В этом разделе мы создали модули, чтобы сделать приложение более управляемым. Мы также создали службы для обработки бизнес-логики приложения, взаимодействуя с базой данных и возвращая соответствующие ответы. Наконец, мы создали контроллер и сгенерировали необходимые методы для обработки HTTP-запросов от клиентов, например.GET,POST,PUTа такжеDELETE. В следующем разделе мы завершим настройку серверной части.

Шаг 5 — Создайте дополнительный сертификат для Mongoose

Мы можем передать уникальный идентификатор (также известный какPostID), чтобы различать каждый пост в приложении блога. Это означает, что для получения статьи нам нужно передать этот идентификатор в качестве параметра запроса. Чтобы убедиться в этомpostIDпараметры и чтобы эта статья действительно была доступна в базе данных, нам нужно создать повторно используемую функцию, к которой можно получить доступ изBlogControllerлюбой метод инициализации.

Чтобы настроить его, перейдите к./src/blogпапка:

cd ./src/blog/

Затем создайте файл с именемsharedновая папка для:

mkdir -p shared/pipes

существуетpipesпапку, с помощью текстового редактора создайте новый файл с именем validate-object-id.pipes.ts и откройте его. Добавьте следующее, чтобы определить принятыйpostIDданные:

~/blog-backend/src/blog/shared/pipes/validate-object-id.pipes.ts

import { PipeTransform, Injectable, ArgumentMetadata, BadRequestException } from '@nestjs/common';
import * as mongoose from 'mongoose';

@Injectable()
export class ValidateObjectId implements PipeTransform<string> {
    async transform(value: string, metadata: ArgumentMetadata) {
        const isValid = mongoose.Types.ObjectId.isValid(value);
        if (!isValid) throw new BadRequestException('Invalid ID!');
        return value;
    }
}

ValidateObjectId()класс сделан@nestjs/commonв модулеPipeTransformметод реализован. оно имеетtransform()метод, который принимает значение в качестве параметра - в этом случаеpostID. Используя этот метод, любые данные сpostIDHTTP-запросы внешнего интерфейса в приложении считаются недействительными. Сохраните и закройте файл.

После создания службы и контроллера нам нужно создатьBlogSchemaизPostМодель. Эта конфигурация может быть в корнеApplicationModule, но в этом примере мы будемBlogModuleМодели встроены для поддержки организации приложения. Открыть./src/blog/blog.module.tsи обновите его с помощью:

~/blog-backend/src/blog/blog.module.ts

import { Module } from '@nestjs/common';
import { BlogController } from './blog.controller';
import { BlogService } from './blog.service';
import { MongooseModule } from '@nestjs/mongoose';
import { BlogSchema } from './schemas/blog.schema';

@Module({
  imports: [
    MongooseModule.forFeature([{ name: 'Post', schema: BlogSchema }])
 ],
  controllers: [BlogController],
  providers: [BlogService]
})
export class BlogModule { }

Здесь мы используемMongooseModule.forFeature()метод, чтобы определить, какие модели должны быть зарегистрированы в модуле. Если этот метод недоступен, используйте@injectModel()декоратор вBlogServiceинъекцияPostModelне будет работать. Когда вы закончите добавлять, сохраните и закройте файл.

На этом этапе мы создали полный бэкэнд RESTful API с помощью Nest.js и интегрировали его с MongoDB. В следующем разделе мы настроим сервер для разрешения HTTP-запросов с других серверов, поскольку наше внешнее приложение и серверная часть будут работать на разных портах.

Шаг 6. Включите CORS

HTTP-запросы между источниками обычно блокируются по умолчанию, если сервер не разрешает это. Чтобы интерфейсные приложения могли выполнять запросы из разных источников на внутренние серверы, эта функция должна быть включена.Совместное использование ресурсов между источниками (CORS), метод, который позволяет запрашивать ресурсы из разных источников на веб-странице.

Чтобы включить CORS в Nest.js, нам нужноmain.tsДобавьте метод в файл. Откройте его текстовым редактором по адресу./src/main.tsфайл и обновите его следующим образом:

~/blog-backend/src/main.ts

import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  app.enableCors();
  await app.listen(3000);
}
bootstrap();

Сохраните и закройте файл.

Теперь, когда у нас есть настройка серверной части, мы переместим наше внимание на внешний интерфейс, используя Vue.js для использования созданного API.

Шаг 7 — Создайте внешний интерфейс Vue.js

В этом разделе мы создадим интерфейсное приложение с использованием Vue.js.Vue CLI— это каркас, который позволяет нам быстро и легко создавать и устанавливать новый проект Vue.js.

Во-первых, вам необходимо глобально установить Vue CLI на свой компьютер. Откройте другой терминал, обратите внимание, что путь не вblog-backendпапку, но в папку разработки вашего локального проекта, затем запустите:

npm install -g @vue/cli

После завершения процесса установки мы будем использоватьvueКоманда создает новый проект Vue.js:

vue create blog-frontend

После ввода этой команды мы увидим краткую подсказку. выберитеmanually select features(имеется в виду ручной выбор функции), затем нажмите кнопку空格, отображается ряд свойств, позволяющих выбрать свойства, необходимые для этого проекта. мы выберемBabel,Routerа такжеLinter / Formatter.

Alt CLI 初始化 Vue 项目

Для следующей инструкции введитеyиспользовать режим истории маршрута; это включит режим истории в файле маршрутизатора, который будет автоматически сгенерирован для нашего проекта. Кроме того, выберите толькоESLint with error prevention onlyКонфигурация для линтера/форматтера. Далее выберитеLint on saveЧтобы сохранить другие функции Lint. Затем выберите сохранение нашей конфигурации вdedicated config file(частный файл конфигурации) для будущих проектов. Наконец, введите имя для наших пресетов, напримерvueconfig.

Alt CLI 初始化 Vue.js 项目的最后一步

Vue.js запустится в файле с именемblog-frontendкаталог для создания приложения и всех его необходимых зависимостей.

После завершения процесса установки найдите в приложении Vue.js:

cd blog-frontend

Затем запустите сервер с помощью следующей команды:

npm run serve

Наше приложение будет вhttp://localhost:8080запускать на.

Alt Vue.js 首页界面

Поскольку в этом приложении мы будем делать HTTP-запросы, нам нужно установить axios, HTTP-клиент на основе Promise для браузера. Здесь axios будут использоваться для выполнения HTTP-запросов от разных компонентов приложения. На терминале вашего компьютера нажмитеCTRL + CЗавершите интерфейсное приложение, затем выполните следующие команды:

npm install axios --save

Наше внешнее приложение будет выполнять вызовы API к внутреннему API в определенном домене из разных компонентов приложения. Чтобы убедиться, что структура запроса маршрутизации нашего приложения верна, мы можем создать辅助файл, в котором определен серверbaseURL.

Во-первых, в терминале, который все еще находится в передней части блога, найдите./src/папка:

cd ./src/

Создайте другое имяutilsпапка:

mkdir utils

существуетutilsпапку, с помощью текстового редактора создайте файл с именемhelper.jsновый файл и откройте его. Добавьте следующее, чтобы определить серверный проект Nest.js.baseURL:

~blog-frontend/src/utils/helper.js

export const server = {

baseURL: 'http://localhost:3000'

}

ОпределенныйbaseURLПосле этого мы можем вызвать его из любого места в файле компонента Vue.js. В случае необходимости изменения URL-адреса проще изменить базовый URL-адрес в этом файле, чем обновлять его во всем коде приложения.

В этом разделе мы установили Vue CLI, инструмент для создания новых приложений Vue.js. Мы используем этот инструмент для созданияblog-frontendзаявление. Кроме того, мы запустили приложение и установили библиотеку под названием axios, которую мы используем всякий раз, когда в приложении происходит вызов HTTP. Далее мы создадим компоненты для приложения.

Шаг 8 — Создание повторно используемых компонентов

Теперь мы собираемся создать повторно используемые компоненты для нашего приложения, которое является стандартной структурой приложения Vue.js. Система компонентов в Vue.js позволяет разработчикам создавать единую автономную единицу интерфейса с собственным состоянием, HTML и стилями. Это позволяет повторно использовать эти компоненты.

Каждый компонент Vue.js состоит из трех отдельных частей:

  • <template>: содержит HTML-контент
  • <script>: содержит всю базовую интерфейсную логику и определяет функции
  • <style>: отдельные таблицы стилей для каждого компонента

Сначала создадим компонент для создания статей. мы должны./src/componentsСоздайте папку с именемpostНовая папка для необходимых повторно используемых компонентов о статье. Затем используйте текстовый редактор во вновь созданномpostСоздайте еще один файл в папке и назовите егоCreate.vue. Откройте этот файл и добавьте следующий код, который сообщает нам поля ввода, которые нам нужны для отправки нашей статьи:

~blog-frontend/src/components/post/Create.vue

<template>
   <div>
        <div class="col-md-12 form-wrapper">
          <h2> Create Post </h2>
          <form id="create-post-form" @submit.prevent="createPost">
               <div class="form-group col-md-12">
                <label for="title"> Title </label>
                <input type="text" id="title" v-model="title" name="title" class="form-control" placeholder="Enter title">
               </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-12">
                  <label for="body"> Write Content </label>
                  <textarea id="body" cols="30" rows="5" v-model="body" class="form-control"></textarea>
              </div>
              <div class="form-group col-md-12">
                  <label for="author"> Author </label>
                  <input type="text" id="author" v-model="author" name="author" class="form-control">
              </div>

              <div class="form-group col-md-4 pull-right">
                  <button class="btn btn-success" type="submit"> Create Post </button>
              </div>          
          </form>
        </div>
    </div>
</template>

ЭтоCreatePostкомпонент<template>часть. Он содержит ввод HTML-элемента, необходимый для создания новой статьи. Каждое поле ввода имеетv-modelДирективы как входные свойства. Это делается для того, чтобы включить двустороннюю привязку данных для полей ввода в каждой форме, чтобы Vue.js было проще получать ввод пользователя.

Далее будет<script>Раздел добавляется непосредственно в предыдущий файл:

~blog-frontend/src/components/post/Create.vue

...
<script>
import axios from "axios";
import { server } from "../../utils/helper";
import router from "../../router";
export default {
  data() {
    return {
      title: "",
      description: "",
      body: "",
      author: "",
      date_posted: ""
    };
  },
  created() {
    this.date_posted = new Date().toLocaleDateString();
  },
  methods: {
    createPost() {
      let postData = {
        title: this.title,
        description: this.description,
        body: this.body,
        author: this.author,
        date_posted: this.date_posted
      };
      this.__submitToServer(postData);
    },
    __submitToServer(data) {
      axios.post(`${server.baseURL}/blog/post`, data).then(data => {
        router.push({ name: "home" });
      });
    }
  }
};
</script>

Здесь мы добавляем файл с именемcreatePost()способ создать новую статью и отправить ее на сервер с помощью axios. Как только пользователь создаст новую статью, приложение перенаправит его обратно на домашнюю страницу, где пользователь сможет просмотреть список созданных статей.

Позже в этом руководстве мы настроим vue-router для реализации перенаправления.

Сохраните и закройте файл, когда закончите редактирование. Полное о приложенииCreate.vueдокументы, пожалуйста, посетитеDO Community repository.

Теперь нам нужно создать еще один компонент для редактирования конкретной статьи. Таргетинг./src/components/postпапку, создайте новую с именемEdit.vueдокумент. добавить следующее<template>Часть кода в файле:

~blog-frontend/src/components/post/Edit.vue

<template>
<div>
      <h4 class="text-center mt-20">
       <small>
         <button class="btn btn-success" v-on:click="navigate()"> View All Posts </button>
       </small>
    </h4>
        <div class="col-md-12 form-wrapper">
          <h2> Edit Post </h2>
          <form id="edit-post-form" @submit.prevent="editPost">
            <div class="form-group col-md-12">
                <label for="title"> Title </label>
                <input type="text" id="title" v-model="post.title" name="title" class="form-control" placeholder="Enter title">
            </div>
            <div class="form-group col-md-12">
                <label for="description"> Description </label>
                <input type="text" id="description" v-model="post.description" name="description" class="form-control" placeholder="Enter Description">
            </div>
            <div class="form-group col-md-12">
                <label for="body"> Write Content </label>
                <textarea id="body" cols="30" rows="5" v-model="post.body" class="form-control"></textarea>
            </div>
            <div class="form-group col-md-12">
                <label for="author"> Author </label>
                <input type="text" id="author" v-model="post.author" name="author" class="form-control">
            </div>

            <div class="form-group col-md-4 pull-right">
                <button class="btn btn-success" type="submit"> Edit Post </button>
            </div>
          </form>
        </div>
    </div>
</template>

Содержимое раздела шаблона здесь такое же, как иCreatePost()Компоненты аналогичны, единственное отличие состоит в том, что он содержит конкретное содержание конкретной статьи, которую необходимо отредактировать.

Далее непосредственно вEdit.vueсередина</template>добавить после части<script>часть:

~blog-frontend/src/components/post/Edit.vue

...
<script>
import { server } from "../../utils/helper";
import axios from "axios";
import router from "../../router";
export default {
  data() {
    return {
      id: 0,
      post: {}
    };
  },
  created() {
    this.id = this.$route.params.id;
    this.getPost();
  },
  methods: {
    editPost() {
      let postData = {
        title: this.post.title,
        description: this.post.description,
        body: this.post.body,
        author: this.post.author,
        date_posted: this.post.date_posted
      };

      axios
        .put(`${server.baseURL}/blog/edit?postID=${this.id}`, postData)
        .then(data => {
          router.push({ name: "home" });
        });
    },
    getPost() {
      axios
        .get(`${server.baseURL}/blog/post/${this.id}`)
        .then(data => (this.post = data.data));
    },
    navigate() {
      router.go(-1);
    }
  }
};
</script>

Здесь мы получаем параметры маршрутаidдля обозначения конкретной статьи. Затем мы создалиgetPost()метод для извлечения сведений об этой статье из базы данных и использования их для обновления страницы. Наконец, мы создалиeditPost()метод для отправки отредактированной статьи обратно на внутренний сервер с помощью HTTP-запроса PUT.

Сохраните и закройте файл, когда закончите редактирование. Полное о приложенииEdit.vueдокументы, пожалуйста, посетитеDO Community repository.

Теперь мы./src/components/postСоздайте папку с именемPost.vueновые компоненты. Это позволяет нам просматривать детали конкретной статьи с главной страницы. Затем добавьте следующее вPost.vueсередина:

~blog-frontend/src/components/post/Post.vue

<template>
    <div class="text-center">
        <div class="col-sm-12">
      <h4 style="margin-top: 30px;"><small><button class="btn btn-success" v-on:click="navigate()"> View All Posts </button></small></h4>
      <hr>
      <h2>{{ post.title }}</h2>
      <h5><span class="glyphicon glyphicon-time"></span> Post by {{post.author}}, {{post.date_posted}}.</h5>
      <p> {{ post.body }} </p>

    </div>
    </div>
</template>

Этот код будет отображать детали статьи, в том числе标题(titile),作者(author)и статьи正文(body).

Теперь непосредственно в</template>После этого добавьте следующий код:

~blog-frontend/src/components/post/Post.vue

...
<script>
import { server } from "../../utils/helper";
import axios from "axios";
import router from "../../router";
export default {
  data() {
    return {
      id: 0,
      post: {}
    };
  },
  created() {
    this.id = this.$route.params.id;
    this.getPost();
  },
  methods: {
    getPost() {
      axios
        .get(`${server.baseURL}/blog/post/${this.id}`)
        .then(data => (this.post = data.data));
    },
    navigate() {
      router.go(-1);
    }
  }
};
</script>

здесь<script>Содержимое части аналогично компоненту специальной статьи редактора, параметры получаем из роутаidи используйте его для получения сведений о конкретной статье.

Сохраните и закройте файл, когда закончите редактирование. Полное о приложенииPost.vueдокументы, пожалуйста, посетитеDO Community repository.

Далее, вы хотите отобразить все созданные статьи пользователю, нам нужно создать новый компонент. Таргетингsrc/viewsсерединаviewsпапка, вы увидитеHome.vueКомпонент. Если этот файл не существует, создайте его с помощью текстового редактора и добавьте следующий код:

~blog-frontend/src/views/Home.vue

<template>
    <div>

      <div class="text-center">
        <h1>Nest Blog Tutorial</h1>
       <p> This is the description of the blog built with Nest.js, Vue.js and MongoDB</p>

       <div v-if="posts.length === 0">
            <h2> No post found at the moment </h2>
        </div>
      </div>

        <div class="row">
           <div class="col-md-4" v-for="post in posts" :key="post._id">
              <div class="card mb-4 shadow-sm">
                <div class="card-body">
                   <h2 class="card-img-top">{{ post.title }}</h2>
                  <p class="card-text">{{ post.body }}</p>
                  <div class="d-flex justify-content-between align-items-center">
                    <div class="btn-group" style="margin-bottom: 20px;">
                      <router-link :to="{name: 'Post', params: {id: post._id}}" class="btn btn-sm btn-outline-secondary">View Post </router-link>
                       <router-link :to="{name: 'Edit', params: {id: post._id}}" class="btn btn-sm btn-outline-secondary">Edit Post </router-link>
                       <button class="btn btn-sm btn-outline-secondary" v-on:click="deletePost(post._id)">Delete Post</button>
                    </div>
                  </div>

                  <div class="card-footer">
                    <small class="text-muted">Posted on: {{ post.date_posted}}</small><br/>
                    <small class="text-muted">by: {{ post.author}}</small>
                  </div>

                </div>
              </div>
            </div>
      </div>
    </div>
</template>

здесь, в<template>участок, черезpost._idпараметры, мы используем<router-link>создавать ссылки для редактирования и просмотра статей. мы также использовалиv-ifДирективы выборочно отображают статьи для пользователя. Если в базе нет статей, пользователь увидит только следующий текст:На данный момент пост не найден.

Сохраните и закройте файл, когда закончите редактирование. Полное о приложенииHome.vueдокументы, пожалуйста, посетитеDO Community repository.

Теперь непосредственно вHome.vueсередина</template>После части добавьте следующее</script>часть:

~blog-frontend/src/views/Home.vue

...
<script>
// @ 是 /src 的别名
import { server } from "@/utils/helper";
import axios from "axios";

export default {
  data() {
    return {
      posts: []
    };
  },
  created() {
    this.fetchPosts();
  },
  methods: {
    fetchPosts() {
      axios
        .get(`${server.baseURL}/blog/posts`)
        .then(data => (this.posts = data.data));
    },
    deletePost(id) {
      axios.delete(`${server.baseURL}/blog/delete?postID=${id}`).then(data => {
        console.log(data);
        window.location.reload();
      });
    }
  }
};
</script>

в этом файле<script>раздел, мы создалиfetchPosts()метод для получения всех статей из базы данных и обновления страницы данными, возвращенными сервером.

Теперь мы обновим внешний интерфейс приложения.Appкомпоненты для созданияHomeкомпоненты иCreateСсылка на компонент. Открытьsrc/App.vue, обновите его с помощью:

~blog-frontend/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>
#app {
  font-family: "Avenir", Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  color: #2c3e50;
}
#nav {
  padding: 30px;
  text-align: center;
}

#nav a {
  font-weight: bold;
  color: #2c3e50;
}

#nav a.router-link-exact-active {
  color: #42b983;
}
</style>

В приведенном выше коде, кроме включения вHomeа такжеCreateПомимо ссылки на компонент, он также содержит<Style>Раздел, который является таблицей стилей для этого компонента, содержит определения стилей для некоторых элементов на странице. Сохраните и закройте файл.

В этом разделе мы создали все компоненты, необходимые приложению. Далее мы настроим файл маршрутизации.

Шаг 9 — Постройте маршрут

После создания всех необходимых повторно используемых компонентов теперь мы можем правильно настроить файл маршрутизации, обновив файл маршрута, содержащий ссылки на все компоненты. Это гарантирует, что все маршруты во внешнем приложении будут сопоставлены с определенными компонентами, чтобы можно было предпринять соответствующие действия. Таргетинг./src/router.js, и замените его содержимое следующим:

~blog-frontend/src/router.js

import Vue from 'vue'
import Router from 'vue-router'
import HomeComponent from '@/views/Home';
import EditComponent from '@/components/post/Edit';
import CreateComponent from '@/components/post/Create';
import PostComponent from '@/components/post/Post';

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 },
    { path: '/post/:id', name: 'Post', component: PostComponent }
  ]
});

мы начинаем сvue-routerимпортируется в модульRouter, и, пройдяmodeа такжеrouteПараметр создает его экземпляр.vue-routerРежим по умолчанию — хеш-режим, который использует хэш URL-адреса для имитации полного URL-адреса, поэтому страница не перезагружается при изменении URL-адреса. Если хеш-режим не нужен, мы можем использовать здесь режим истории, чтобы реализовать маршрутизацию URL-адресов без перезагрузки страницы. Наконец, вroutesВ опциях указываем конкретный соответствующий компонент маршрута — компонент и имя компонента, который должен отрисовываться при вызове маршрута в приложении. Сохраните и закройте файл.

Теперь, когда мы настроили маршрутизацию приложения, нам нужно импортировать файл Bootstrap, чтобы предварительно настроить пользовательский интерфейс приложения. Нам нужно открыть в текстовом редакторе./public/index.htmlфайл и включите файлы CDN для Bootstrap, добавив в файл следующее:

~blog-frontend/public/index.html

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

сохраните и выйдите из файла, затем используйтеnpm run serveЗа нашихblog-frontendПроект перезапускает приложение, если оно в данный момент не запущено.

Уведомление:Убедитесь, что и внутренний сервер, и экземпляр MongoDB запущены. Если нет, перейдите от другого нового терминала кblog-backendпод проект и запуститьnpm run start. Опять же, запустив с нового терминалаsudo mongodдля запуска службы MongoDB.

Перейти к нашему приложению по URL-адресу:http://localhost:8080. Теперь вы можете протестировать свой блог, создавая и редактируя статьи!

Alt 创建一篇新文章

Нажмите на приложениеCreateсмотретьCreate Postвид, который аналогиченCreateComponentКомпонент связан и будет отображать этот компонент. Введите что-нибудь в поле ввода и нажмитеCreate Postкнопка отправки статьи. После этого приложение перенаправит вас обратно на домашнюю страницу.

На главной странице приложения отображаются компонентыHomeComponent. Этот компонент вызовет один из своих методов и отправит HTTP-вызов, чтобы получить все статьи из базы данных и отобразить их пользователю.

Alt 从数据库中查看所有的文章

Нажмите на конкретную статьюEdit Postи вы попадете на страницу редактирования, где сможете внести любые изменения и сохранить свою статью.

Alt 修改一篇新发的文章

В этом разделе мы настраиваем и настраиваем маршрутизацию для приложения. На этом наше приложение для блога готово.

Суммировать

В этом руководстве вы изучили новые способы структурирования приложений Node.js с помощью Nest.js. Вы создали простое приложение для блога, построили серверный RESTful API с помощью Nest.js и обработали всю логику внешнего интерфейса с помощью Vue.js. Кроме того, вы интегрируете базу данных MongoDB в свое приложение Nest.js.

Чтобы узнать, как добавить аутентификацию в ваше приложение, вы можете использоватьPassport.js. Популярная библиотека аутентификации для Node.js. ты сможешьДокументация Nest.jsУзнайте об интеграции Passport.js в .

ты сможешьGitHub для этого проектаНайдите полный исходный код проекта на . Хотите получить больше информации о Nest. вы можете посетитьофициальная документация.

Если вы обнаружите ошибки в переводе или в других областях, требующих доработки, добро пожаловать наПрограмма перевода самородковВы также можете получить соответствующие бонусные баллы за доработку перевода и PR. начало статьиПостоянная ссылка на эту статьюЭто ссылка MarkDown этой статьи на GitHub.


Программа перевода самородковэто сообщество, которое переводит высококачественные технические статьи из Интернета сНаггетсДелитесь статьями на английском языке на . Охват контентаAndroid,iOS,внешний интерфейс,задняя часть,блокчейн,товар,дизайн,искусственный интеллектЕсли вы хотите видеть более качественные переводы, пожалуйста, продолжайте обращать вниманиеПрограмма перевода самородков,официальный Вейбо,Знай колонку.