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

Автор выбралSoftware in the Public Interest Inc для получения пожертвования в рамках программыWrite for DOnations.

Вступление

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

Vue.js - это интерфейсный JavaScript-фреймворк для создания пользовательских интерфейсов. Он имеет простой, но очень мощный API и отличную производительность. Vue.js is capable of powering the front-end layer and logic of any web application irrespective of the size. Простота интеграции его с другими библиотеками или существующими проектами делает его идеальным выбором для большинства современных веб-приложений.

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

Вы создадите приложение для блога, с помощью которого пользователи смогут создавать и сохранять новые сообщения, просматривать сохраненные сообщения на главной странице и выполнять другие процессы, такие как редактирование и удаление сообщений. Кроме того, вы подключите свое приложение и сохраните его данные с помощьюMongoDB, который представляет собой базу данных NoSQL без схемы, которая может получать и хранить документы JSON. Этот учебник посвящен созданию вашего приложения в среде разработки. В производственной среде также следует учитывать аутентификацию пользователей для вашего приложения.

Предпосылки

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

  • Локальная установкаNode.js (минимум v6) иnpm (минимум v5.2). Node.js is a JavaScript run-time environment that allows you to run your code outside of the browser. Он поставляется с предустановленным менеджером пакетов под названиемnpm, который позволяет вам устанавливать и обновлять пакеты. Чтобы установить их в macOS или Ubuntu 18.04, выполните действия, описанные вHow to Install Node.js and Create a Local Development Environment on macOS или в разделе «Установка с помощью PPA»How To Install Node.js on Ubuntu 18.04.

  • MongoDB установлен на вашей машине. Следуйте инструкциямhere, чтобы загрузить и установить его для выбранной вами операционной системы. Чтобы успешно установить MongoDB, вы можете либо установить его, используяHomebrew наMac, либо загрузив его изMongoDB website.

  • Базовое понимание TypeScript иJavaScript.

  • Установлен текстовый редактор, напримерVisual Studio Code,Atom илиSublime Text.

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

[[шаг-1 -—- install-nest-js-and-other-dependencies]] == Шаг 1. Установка Nest.js и других зависимостей

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

npm i -g @nestjs/cli

Вы увидите вывод, похожий на следующий:

Output@nestjs/[email protected]
added 220 packages from 163 contributors in 49.104s

Чтобы подтвердить установку CLI-интерфейса Nest, выполните следующую команду на своем терминале:

nest --version

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

Output5.8.0

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

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

nest new blog-backend

Сразу после выполнения командыnest предложит вам предоставить некоторую базовую информацию, такую ​​какdescription,version иauthor. Идите вперед и предоставьте соответствующие детали. НажмитеENTER на своем компьютере, чтобы продолжить после ответа на каждый запрос.

Далее вы выберете менеджер пакетов. Для целей этого руководства выберитеnpm и нажмитеENTER, чтобы начать установку Nest.js.

Alt Creating a Nest project

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

Далее перейдите к новой папке проекта из вашего терминала:

cd blog-backend

Выполните следующую команду для установки других зависимостей сервера:

npm install --save @nestjs/mongoose mongoose

Вы установили@nestjs/mongoose, специальный пакет Nest.js для инструмента моделирования объектов для MongoDB, иmongoose, который является пакетом для Mongoose.

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

npm run start

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

Alt Welcome page of the fresh installation of Nest.js application

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

[[шаг-2 -—- настройка-и-соединение-с-базой данных]] == Шаг 2 - Настройка и подключение к базе данных

На этом этапе вы сконфигурируете и интегрируете MongoDB в ваше приложение Nest.js. Вы будете использовать MongoDB для хранения данных для вашего приложения. MongoDB хранит свои данные вdocuments как парыfield : value. Для доступа к этой структуре данных вы будете использоватьMongoose, который представляет собой объектное моделирование документов (ODM), позволяющее определять схемы, представляющие типы данных, которые хранятся в базе данных MongoDB.

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

sudo mongod

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

Откройте проектblog-backend в текстовом редакторе и перейдите к./src/app.module.ts. Вы можете настроить соединение с базой данных, включив установленныйMongooseModule в корневой каталогApplicationModule. Для этого обновите содержимое в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() для подключения к базе данных. Сохраните и закройте файл, когда вы закончите редактирование.

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

[[step-3 -—- created-a-database-schema-interfaces-and-dto]] == Шаг 3. Создание схемы базы данных, интерфейсов и DTO

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

  • database schema: это организация данных как образец для определения структуры и типов данных, которые база данных должна хранить.

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

  • data transfer object: это объект, который определяет, как данные будут отправляться по сети, и переносит данные между процессами.

Для начала вернитесь к своему терминалу, на котором в настоящее время работает приложение, и остановите процесс с помощью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 как строковые значения. Сохраните и выйдите из файла.

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

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

Вернитесь в папкуblog:

cd ~/blog-backend/src/blog/

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

mkdir dto

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

~/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, чтобы избежать ненужных изменений. Сохраните и выйдите из файла, когда вы закончите редактирование.

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

[[step-4 -—- created-the-module-controller-and-service-for-the-blog]] == Шаг 4. Создание модуля, контроллера и службы для блога

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

Генерация модуля

Как и в интерфейсной веб-среде Angular, Nest.js использует модульный синтаксис. Nest.js applications have a modular design; it comes installed with a single root module, which is often sufficient for a small application. Но когда приложение начинает расти, Nest.js рекомендует многокомпонентную организацию, разбивая код на связанные функции.

module в Nest.js идентифицируется декоратором@Module() и принимает объект со свойствами, такими какcontrollers иproviders. Каждое из этих свойств принимает массив изcontrollers иproviders соответственно.

Вы создадите новый модуль для этого приложения блога, чтобы структура была более организованной. Для начала, все еще находясь в папке~/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 необходимыми свойствами позже в руководстве. Сохраните и выйдите из файла.

Генерация Сервиса

service, который также может называться поставщиком в 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) { }

    async getPosts(): Promise {
        const posts = await this.postModel.find().exec();
        return posts;
    }

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

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

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

    async deletePost(postID): Promise {
        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 из внутреннего API. Теперь вы создадите необходимые маршруты, которые будут обрабатывать HTTP-вызовы от клиентского интерфейса.

Генерация контроллера

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

Чтобы удовлетворить все 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 - это файлы TypeScript, украшенные метаданными@Controller. Команда также импортировала только что созданный контроллер и добавила его в модуль блога.

Затем откройте файл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
        })
    }
}

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

Наконец, вы создали следующие асинхронные методы:

  • getPosts(): этот метод будет выполнять функцию получения HTTP-запроса GET от клиента, чтобы получить все сообщения из базы данных, а затем вернуть соответствующий ответ. Он украшен@Get('posts').

  • getPost(): это принимаетpostID в качестве параметра и извлекает одно сообщение из базы данных. В дополнение к параметруpostID, переданному этому методу, вы реализовали добавление дополнительного метода с именемValidateObjectId(). Этот метод реализует интерфейсPipeTransform из Nest.js. Его цель - проверить и гарантировать, что параметрpostID можно найти в базе данных. Вы определите этот метод в следующем разделе.

  • addPost(): этот метод будет обрабатывать HTTP-запрос POST для добавления нового сообщения в базу данных.

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

~/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. Это означает, что любой запрос GET, отправленный в конечную точкуblog/posts (http:localhost:3000/blog/posts), будет обрабатываться методом getPosts (). Этот пример похож на то, как другие методы будут обрабатывать HTTP-запросы.

Сохраните и выйдите из файла.

Чтобы получить полный файлblog.controller.ts, посетитеDO Community repository для этого приложения.

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

[[step-5 -—- created-an-extra-validation-for-mongoose]] == Шаг 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 {
    async transform(value: string, metadata: ArgumentMetadata) {
        const isValid = mongoose.Types.ObjectId.isValid(value);
        if (!isValid) throw new BadRequestException('Invalid ID!');
        return value;
    }
}

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

После создания службы и контроллера вам необходимо настроить модельPost, основанную наBlogSchema. Эту конфигурацию можно настроить в корневом каталоге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(), чтобы определить, какие модели должны быть зарегистрированы в модуле. Без этого введениеPostModel вBlogService с использованием декоратора@injectModel() не сработало бы. Сохраните и закройте файл, когда вы закончите добавлять контент.

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

[[step-6 -—- enable-cors]] == Шаг 6. Включение CORS

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

В Nest.js для включения CORS вам нужно добавить единственный метод в ваш файл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, созданных до сих пор.

[[step-7 -—- created-the-vue-js-frontend]] == Шаг 7 - Создание внешнего интерфейса Vue.js

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

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

npm install -g @vue/cli

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

vue create blog-frontend

Вы увидите короткое приглашение после ввода этой команды. Выберите вариантmanually select features, а затем выберите функции, которые вам понадобятся для этого проекта, нажавSPACE на своем компьютере, чтобы выделить несколько функций. ВыберитеBabel,Router иLinter / Formatter.

Alt Vue project CLI set up

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

Alt Vue.js final CLI set up

Vue.js will then start creating the application and all its required dependencies in a directory named blog-frontend.

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

cd blog-frontend

Затем запустите сервер разработки с:

npm run serve

Ваше приложение будет работать наhttp://localhost:8080.

Alt Vue.js home view

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

npm install axios --save

Ваше интерфейсное приложение будет выполнять API-вызов к внутреннему API в определенном домене из разных компонентов приложения. Чтобы обеспечить правильную структуру для этого приложения, вы можете создать файлhelper и определить серверbaseURL.

Для начала, находясь в терминалеblog-frontend, перейдите в папку./src/:

cd ./src/

Создайте еще одну папку с именемutils:

mkdir utils

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

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

export const server = {

baseURL: 'http://localhost:3000'

}

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

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

[[step-8 -—- Creating-reusable-components]] == Шаг 8 - Создание повторно используемых компонентов

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

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

  • <template>: содержит содержимое HTML

  • <script>: содержит всю базовую логику внешнего интерфейса и определяет функции

  • <style>: таблица стилей для каждого отдельного компонента

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

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

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

Затем добавьте раздел<script> в тот же файл сразу после предыдущего содержимого:

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

...

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

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

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

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

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

Этот раздел шаблона содержит такое же содержимое, что и компонентCreatePost(); Единственное отличие состоит в том, что он содержит подробную информацию о конкретном посте, который необходимо отредактировать.

Затем добавьте раздел `

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

Сохраните и выйдите из файла. Чтобы получить полный файлEdit.vue, посетитеDO Community repository для этого приложения.

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

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

Этот код отображает детали сообщения, которое включаетtitle,author и сообщениеbody.

Теперь, следуя за</template>, добавьте в файл следующий код:

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

...

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

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

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

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

Здесь, в разделе<template>, вы использовали<router-link> для создания ссылки для редактирования, а также для просмотра сообщения, передавpost._id в качестве параметра запроса. Вы также использовали директивуv-if для условного рендеринга сообщения для пользователей. Если в базе нет сообщений, пользователь увидит только этот текст:No post found at the moment.

Сохраните и выйдите из файла. Чтобы получить полный файлHome.vue, посетитеDO Community repository для этого приложения.

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

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

...

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

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

~blog-frontend/src/App.vue



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

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

[[step-9 -—- setting-up-routing]] == Шаг 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 }
  ]
});

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

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

~blog-frontend/public/index.html




  ...
  
  blog-frontend


   ...

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

[.note] #Note: Убедитесь, что и внутренний сервер, и экземпляр MongoDB также работают. В противном случае перейдите кblog-backend с другого терминала и запуститеnpm run start. Также запустите службу MongoDB, запустивsudo mongod с нового терминала.
#

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

Alt Create a new post

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

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

Alt View all posts from the database

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

Alt Edit a new post

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

Заключение

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

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

Вы можете найти полный исходный код этого проектаhere on GitHub. Для получения дополнительной информации о Nest.js вы можете посетитьofficial documentation.

Related