Заполнение баз данных с помощью NestJS

NestJS — это хорошо масштабируемая платформа NodeJS, революционизирующая создание серверных приложений. Его способность создавать контексты приложений позволяет разработчикам легко создавать микроприложения и использовать преимущества компонентов и модулей в этой среде.

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

Что посеять? ?

Любые статические данные, которые обычно используются между объектами или используются в основном для тестирования, — это то, на чем следует сосредоточиться при заполнении. Это могут быть базовые сущности, такие как языки, или сущности, такие как учетные записи администраторов, которые в основном требуются, когда ваше приложение включает аутентификацию и управление ролями пользователей ?‍♂️.

Язык (а), являющийся одним из самых простых объектов для объяснения, — это то, на чем мы собираемся сегодня сосредоточиться.

Структура каталогов ?

Nest придерживается строгого модульного подхода, которого мы будем придерживаться. Вот как мы будем хранить все файлы, описанные в этой статье.

src /
├── database
│   └── seeders
│       ├── seeder.module.ts
│       ├── seeder.ts
│       └── language
│           ├── data.ts
│           ├── languages.module.ts
│           └── languages.service.ts
├── models
│   └── language
│       ├── entities
│       │   └── language.entity.ts
│       └── interfaces
│           └── language.interface.ts
├── providers
│   └── database
│       └── mysql
│           └── provider.module.ts
└── seed.ts

Провайдеры

Прежде чем мы начнем, нам нужно подключиться к базе данных. В этом посте мы собираемся использовать TypOrmModule для подключения к базе данных MySQL, однако вы можете создавать своих собственных провайдеров аналогично MongoDB с использованием MongooseModule. Если ваше приложение требует подключения к нескольким базам данных, подумайте о прочтении Динамических модулей.

См. также:  Code With Me - никогда больше не покидайте свой чердак

provider.module.ts

/**
 * Import and provide base typeorm (mysql) related classes.
 *
 * @module
 */
@Module({
  imports: [
    TypeOrmModule.forRootAsync({
      imports: [MysqlConfigModule],
      useFactory: async (mysqlConfigService: MysqlConfigService) => ({
        type: 'mysql' as DatabaseType,
        host: mysqlConfigService.host,
        port: mysqlConfigService.port,
        username: mysqlConfigService.username,
        password: mysqlConfigService.password,
        database: mysqlConfigService.database,
        entities: [Language],
        synchronize: true,
      }),
      inject: [MysqlConfigService],
    } as TypeOrmModuleAsyncOptions),
  ],
})
export class MysqlDatabaseProviderModule {}

Обратите внимание, как мы используем мою Language Модель непосредственно в массиве сущностей. Это отличается от техники, упомянутой здесь, особенно из-за проблемы, с которой обычно сталкиваются при создании производственных сборок, размещенных на Github.

Модели и интерфейсы

Связывание модели с интерфейсом — хорошая практика. Благодаря поддержке Typescript из коробки, Nest упрощает для нас эту задачу.

Интерфейсы

Это простой пример того, как мы можем создать языковой интерфейс. Здесь особо нечего объяснять, но если в вашей модели есть типы и т. Д., Вы можете воспользоваться преимуществами Enums, а затем привязать их к вашей модели.

languages.interface.ts

/**
 * Language variable type declaration.
 *
 * @interface
 */
export interface ILanguage {
  name: string;
}

Модель / сущность

Всегда полезно иметь в модели временные метки. В остальном создать модель относительно просто. Вы можете узнать больше о том, как создавать модели с помощью TypeORM здесь: Документация TypeORM.

languages.entity.ts

/**
 * Entity Schema for Languages.
 *
 * @class
 */
@Entity({
  name: 'languages',
})
export class Language implements ILanguage {
  @PrimaryGeneratedColumn()
  id: number;
  @Column()
  name: string;
  @Column({
    type: 'timestamp',
    default: () => 'CURRENT_TIMESTAMP',
  })
  createdAt: string;
  @Column({
    type: 'timestamp',
    default: () => 'CURRENT_TIMESTAMP',
  })
  updatedAt: string;
}

Данные

Чтобы упростить задачу, мы будем сохранять все данные Language в константе. Однако, если вы планируете использовать фейкер, подумайте о том, чтобы взглянуть на документацию Faker.

data.ts

export const languages: ILanguage[] = [
  { name: 'English' },
  { name: 'French' },
  { name: 'Spanish' },
  { name: 'Russian' },
  // ... and others ... 
];

Услуги и модули

Сервисы действуют как посредник между контроллером и репозиторием. Это термин, который чаще всего используется при поиске в Интернете шаблона репозитория службы. Подробнее о том, как создавать модули в NestJS, вы можете прочитать здесь.

См. также:  Объявление переменной: использование var, let и const.

Языковая служба

Давайте начнем с создания LanguageService, который будет использовать свой репозиторий в качестве зависимости. Однако репозиторий предоставляется нам самим TypeORM, поэтому нам не нужно его создавать.

languages.services.ts

/**
 * Service dealing with language based operations.
 *
 * @class
 */
@Injectable()
export class LanguageSeederService {
  /**
   * Create an instance of class.
   *
   * @constructs
   *
   * @param {Repository<Language>} languageRepository
   */
  constructor(
    @InjectRepository(Language)
    private readonly languageRepository: Repository<Language>,
  ) {}
  /**
   * Seed all languages.
   *
   * @function
   */
  create(): Array<Promise<Language>> {
    return languages.map(async (language: ILanguage) => {
      return await this.languageRepository
        .findOne({ name: language.name })
        .exec()
        .then(async dbLangauge => {
          // We check if a language already exists.
          // If it does don't create a new one.
          if (dbLangauge) {
            return Promise.resolve(null);
          }
          return Promise.resolve(
            await this.languageRepository.create(language),
          );
        })
        .catch(error => Promise.reject(error));
    });
  }
}

Языковой модуль

Языковой модуль импортирует TypeOrmModule для сущности Language вместе со своей службой, которая обрабатывает заполнение всех языков.

languages.module.ts

/**
 * Import and provide seeder classes for languages.
 *
 * @module
 */
@Module({
  imports: [TypeOrmModule.forFeature([Language])],
  providers: [LanguageSeederService],
  exports: [LanguageSeederService],
})
export class LanguageSeederModule {}

Главный модуль сеялки

seeder.module.ts

/**
 * Import and provide seeder classes.
 *
 * @module
 */
@Module({
  imports: [MysqlDatabaseProviderModule, LanguageSeederModule],
  providers: [MysqlSeederService, Logger, Seeder],
})
export class SeederModule {}

Обратите внимание, как сюда импортируется наш основной модуль поставщика MySQL. Загрузка всех классов по умолчанию асинхронная, но вы можете сделать их синхронными, импортировав поставщиков в методе, определенном в документации NestJS.

Провайдер сеялки

Это последний класс сидера, который имеет функцию seed(), которая далее вызывает функцию create() в LanguageSeederService.

сеялка.ts

@Injectable()
export class Seeder {
  constructor(
    private readonly logger: Logger,
    private readonly languageSeederService: LanguageSeederService,
  ) {}
  async seed() {
    await this.languages()
      .then(completed => {
        this.logger.debug('Successfuly completed seeding users...');
        Promise.resolve(completed);
      })
      .catch(error => {
        this.logger.error('Failed seeding users...');
        Promise.reject(error);
      });
  }
  async languages() {
    return await Promise.all(this.languageSeederService.create())
      .then(createdLanguages => {
        // Can also use this.logger.verbose('...');
        this.logger.debug(
          'No. of languages created : ' +
            // Remove all null values and return only created languages.
            createdLanguages.filter(
              nullValueOrCreatedLanguage => nullValueOrCreatedLanguage,
            ).length,
        );
        return Promise.resolve(true);
      })
      .catch(error => Promise.reject(error));
  }
}

Создание контекста приложения и запуск сеялки

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

См. также:  Как запустить приложение Angular в качестве контейнера на виртуальной машине Azure

Загрузите свое микроприложение

seed.ts

async function bootstrap() {
  NestFactory.createApplicationContext(SeederModule)
    .then(appContext => {
      const logger = appContext.get(Logger);
      const seeder = appContext.get(Seeder);
      seeder
        .seed()
        .then(() => {
          logger.debug('Seeding complete!');
        })
        .catch(error => {
          logger.error('Seeding failed!');
          throw error;
        })
        .finally(() => appContext.close());
    })
    .catch(error => {
      throw error;
    });
}
bootstrap();

Обратите внимание на то, что у нас есть appContext().close() как последнее действие обещания.

Команда

И последнее, но не менее важное: просто добавьте этот сценарий в свой package.json. Затем вы можете запустить сидер, используя npm или yarn или любой менеджер пакетов, который вы используете.

package.json

{
  // ... other package.json fields ...
  "scripts": {
    // ... other scripts ...
    "seed": "ts-node -r tsconfig-paths/register src/seed.ts"
  }
}

Заключение

Nest — действительно мощный серверный фреймворк. Я твердо верю, что это изменит кодирование в NodeJS. Для получения дополнительной информации о Nest посетите его веб-сайт и подумайте поддержать их в OpenCollective или через Paypal.

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

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

Понравилась статья? Поделиться с друзьями:
IT Шеф
Добавить комментарий

;-) :| :x :twisted: :smile: :shock: :sad: :roll: :razz: :oops: :o :mrgreen: :lol: :idea: :grin: :evil: :cry: :cool: :arrow: :???: :?: :!: