Друзья, всем привет! Меня зовут Игорь Карелин, я frontend-разработчик в компании Домклик. В серии статей мы поэтапно разработаем продвинутое приложение-планировщик. Сначала создадим и настроим монорепозиторий c помощью NX, разработаем интерфейс с помощью React, добавим backend на основе NestJS, и, наконец, подключим базу данных MongoDB.

Будем использовать такие технологии:

  1. NodeJS — программная платформа на движке V8 (компилирующем JavaScript в машинный код), которая превращает JavaScript из узкоспециализированного языка в язык общего назначения.

  2. NX — система инструментов, позволяющая создавать монорепозитории для JavaScript-приложений.

  3. TypeScript — язык программирования для разработки современных веб-приложений, расширяющий возможности традиционного JavaScript.

  4. React — одна из самых популярных JavaScript-библиотек для создания пользовательских интерфейсов.

  5. NestJS — фреймворк, который ускоряет и упрощает разработку масштабируемых серверных приложений на основе программной платформы NodeJS.

  6. MongoDB — документоориентированная система управления базами данных, не требующая описания схемы таблиц. Считается одним из классических примеров NoSQL-систем, использует JSON-подобные документы и схему базы данных.

  7. Docker — контейнеризатор приложений: программное обеспечение для автоматизации развёртывания и управления приложениями в средах с поддержкой контейнеризации.

Сначала установим NodeJs с официального сайта и редактор кода, я буду использовать Visual Studio Code.

Почему монорепозиторий?

Что такое монорепозиторий и для чего он нужен? Монорепозиторий — это стратегия разработки, при которой код нескольких приложений находится в одном репозитории. Это даёт ряд преимуществ:

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

  • Сотрудничество между командами.

Я выбрал монорепозиторий, чтобы не усложнять настройку проекта и иметь возможность легко переиспользовать различные компоненты, интерфейсы и типы. Создадим его с помощью NX — эта технология обеспечивает готовую структуру проекта и продвинутую работу с CLI для лёгкого создания и внедрения приложений. Ознакомиться с NX и узнать о нём подробнее вы можете по этой ссылке.

Создание и настройка проекта

Для создания проекта нам потребуется терминал, я работаю со штатным инструментом MacOS; в зависимости от операционной системы команды могут отличаться. Установите проект NX, в котором сразу будет присутствовать React, далее выберите директорию и выполните команду npx create-nx-workspace@latest --preset=react.

В ходе установки нужно ответить на несколько пунктов:

  1. Выберите любые названия проекта, приложений и препроцессор.

  2. Название проекта: todo-app.

  3. Название React-приложения: frontend.

  4. Я выбрал SASS(.scss)

Готово! Мы только что создали проект NX с приложением React. Теперь установим NestJS. Сначала добавьте плагин Nest в существующую рабочую область. Перейдите в папку проекта и выполните команду npm install -D @nrwl/nest.

Теперь создадим приложение Nest с помощью команды nx g @nrwl/nest:app backend --frontendProject frontend. Мы сгенерировали приложение с названием “backend” и сообщили, что хотим связать его с “frontend”. Благодаря NX нам не нужно вручную настраивать порты, всё будет сделано автоматически.

В файле proxy.conf.json NX сам добавил пути для работы с API. Теперь для получения данных из backend достаточно просто перейти на /api:

proxy.conf.json

{
  "/api": {
    "target": "http://localhost:3333",
    "secure": false
	}
}

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

  • /apps — папка, в которой хранятся наши сгенерированые приложения frontend и backend;

  • /libs — здесь будут храниться части приложения, которые мы можем переиспользовать внутри монорепозитория (ведь одно из преимуществ — это упрощённое управление зависимостями и переиспользование кода между приложениями);

  • /tools — эта папка используется для различных правил или настроек. Например, правил EsLint, работы с автоматизацией и т.д.

Поговорив о создании и структуре проекта, самое время запустить его! И снова идём в терминал: nx run-many --parallel --target=serve --projects=backend,frontend. Мы запустили параллельно несколько проектов без дополнительных настроек. Откройте браузер и проверьте, для этого перейдите по адресу localhost:4200:

и по адресу localhost:4200/api:

Отлично, всё работает!

Установка и подключение к базе данных

Сначала хочу предупредить вас: не стоит хранить в репозиториях файлы *.env и Docker-файлы с секретами! В статье я так делаю для ознакомительных целей.

Мы будем использовать MongoDB, её я выбрал исходя из простоты использования. Подробнее ознакомиться с этой базой данных вы можете на официальном сайте.

Использовать MongoDB можно несколькими способами:

  • Создать удалённую БД на серверах Mongo.

  • Установить MongoDB на свой компьютер.

  • Использовать Docker — этот способ мы и рассмотрим.

Для начала установите Docker на свой компьютер, инструкцию можете посмотреть на официальном сайте. После этого создайте в корне проекта файл docker-compose.yml с такой структурой:

version: '3'
services:
  mongo:
    image: mongo
    container_name: mongo
    restart: always
    environment:
      - MONGO_INITDB_ROOT_USERNAME=admin
      - MONGO_INITDB_ROOT_PASSWORD=admin
    ports:
      - 27017:27017
    volumes:
      - ./mongo-data:/data/db

Подробно не буду останавливаться, про Docker написано множество статей и есть подробная официальная документация. Теперь запустите Docker Compose командой docker-compose up -d. Docker скачает необходимые файлы для правильной работы и в корне проекта появится папка mongo-data, в которой хранятся данные MongoDB, я добавил её в gitignore.

Следующим шагом нужно подключиться к БД из NestJS. Сначала установите необходимые зависимости: библиотеку mongoose для работы с БД и Nest config, который обеспечит работу с файлами *.env: npm install --save mongoose @nestjs/mongoose @nestjs/config.

Я создал папку envs и добавил файл .backend.env, в котором объявил переменные:

DB_LOGIN=admin
DB_PASSWORD=admin
DB_HOST=localhost
DB_PORT=27017
DB_AUTHDATABASE=admin

Теперь создайте конфигурационный файл, который поможет генерировать ссылку для подключения к БД:

db-connect.config.ts

import { ConfigService } from '@nestjs/config';
import { MongooseModuleOptions } from '@nestjs/mongoose';

const getMongoString = (configService: ConfigService) =>
  'mongodb://' +
  configService.get('DB_LOGIN') +
  ':' +
  configService.get('DB_PASSWORD') +
  '@' +
  configService.get('DB_HOST') +
  ':' +
  configService.get('DB_PORT') +
  '/' +
  configService.get('DB_AUTHDATABASE');

const getMongoOptions = () => ({
  useNewUrlParser: true,
  useUnifiedTopology: true,
});

export const getMongoConfig = async (
  configService: ConfigService
): Promise<MongooseModuleOptions> => {
  console.log(getMongoString(configService));
  return {
    uri: getMongoString(configService),
    ...getMongoOptions(),
  };
};

Далее перейдите в app.module.ts и импортируйте в него ConfigModule и MongooseModule. Пара слов о модулях в NestJS: это классы с декоратором Module(), предоставляющим метаданные, которые Nest использует для организации структуры приложения.

В конечном счёте файл будет выглядеть так:

app.module.ts

import { Module } from '@nestjs/common';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { MongooseModule } from '@nestjs/mongoose';
import { getMongoConfig } from '../config/db-connect.config';
import { AppController } from './app.controller';
import { AppService } from './app.service';

@Module({
  imports: [
    ConfigModule.forRoot({
      isGlobal: true, // Позволяет обратиться к env во всем приложении
      envFilePath: 'envs/.backend.env', // Указываем путь до env файла
    }),
    MongooseModule.forRootAsync({ // Модуль для работы с mongo
      imports: [ConfigModule], 
      inject: [ConfigService],
      useFactory: getMongoConfig, // добавляем созданную ранее функцию подключения к БД
    }),
  ],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}

На этом пока остановимся. Мы разобрали настройку проекта и подключение к базе данных для дальнейшей работы. В следующих частях напишем логику работы с базой данных, добавим регистрацию пользователей и т.д. Спасибо за внимание!

Исходный код доступен по ссылке.

Комментарии (2)


  1. Tsykhan
    21.06.2022 11:59

    Все по факту, предельно понятно написано. Жду остальные части


  1. OlegZH
    21.06.2022 12:18
    +1

    Гм. Очень не хватает вводного раздела о том, что именно за планировщик, каковы у него должны быть особенности и т.д. и т.п.

    В серии статей мы поэтапно разработаем продвинутое приложение-планировщик. Сначала создадим и настроим монорепозиторий c помощью NX, разработаем интерфейс с помощью React, добавим backend на основе NestJS, и, наконец, подключим базу данных MongoDB.

    Перед "сначала" ожидалось подробнее обсудить постановку задачи. Пара-тройка абзацев для вхождения в курс дела.