Pour ceux qui sont à l'aide de TypeORM avec Nest.jsvoici une solution pour réaliser vos semis par programmation, à partir de votre code.
Idée:
- Nous avons créer un "semis module" contenant un "semis middleware", qui est responsable de la conduite de l'ensemencement et de veiller à ce que tous les semis se fait avant toute demande de réponse.
- Pour toute demande qui arrive, le semis, middleware l'intercepte et la remet jusqu'à ce qu'il est confirmé que le semis est fait.
- Si la db a été semée, le "seeding middleware" transmet la demande à la prochaine middleware.
- Pour accélérer les choses, le "seeding middleware" conserve un "semis complète" drapeau de l'état de la mémoire pour éviter toute nouvelle db-contrôle après le semis a eu lieu.
Mise en œuvre:
Pour que cela fonctionne, d'abord créer un module qui enregistre un middleware qui est à l'écoute de toutes les demandes entrantes:
// file: src/seeding/SeedingModule.ts
@Module({})
export class SeedingModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
consumer
.apply(SeedingMiddleware)
.forRoutes('*')
}
}
Maintenant, créez le middleware:
// file: src/seeding/SeedingMiddleware.ts
import { Injectable, NestMiddleware } from '@nestjs/common';
import { Request, Response } from 'express';
import { EntityManager } from 'typeorm';
import { SeedingLogEntry } from './entities/SeedingLogEntry.entity';
@Injectable()
export class SeedingMiddleware implements NestMiddleware {
// to avoid roundtrips to db we store the info about whether
// the seeding has been completed as boolean flag in the middleware
// we use a promise to avoid concurrency cases. Concurrency cases may
// occur if other requests also trigger a seeding while it has already
// been started by the first request. The promise can be used by other
// requests to wait for the seeding to finish.
private isSeedingComplete: Promise<boolean>;
constructor(
private readonly entityManager: EntityManager,
) {}
async use(req: Request, res: Response, next: Function) {
if (await this.isSeedingComplete) {
// seeding has already taken place,
// we can short-circuit to the next middleware
return next();
}
this.isSeedingComplete = (async () => {
// for example you start with an initial seeding entry called 'initial-seeding'
// on 2019-06-27. if 'initial-seeding' already exists in db, then this
// part is skipped
if (!await this.entityManager.findOne(SeedingLogEntry, { id: 'initial-seeding' })) {
await this.entityManager.transaction(async transactionalEntityManager => {
await transactionalEntityManager.save(User, initialUsers);
await transactionalEntityManager.save(Role, initialRoles);
// persist in db that 'initial-seeding' is complete
await transactionalEntityManager.save(new SeedingLogEntry('initial-seeding'));
});
}
// now a month later on 2019-07-25 you add another seeding
// entry called 'another-seeding-round' since you want to initialize
// entities that you just created a month later
// since 'initial-seeding' already exists it is skipped but 'another-seeding-round'
// will be executed now.
if (!await this.entityManager.findOne(SeedingLogEntry, { id: 'another-seeding-round' })) {
await this.entityManager.transaction(async transactionalEntityManager => {
await transactionalEntityManager.save(MyNewEntity, initalSeedingForNewEntity);
// persist in db that 'another-seeding-round' is complete
await transactionalEntityManager.save(new SeedingLogEntry('another-seeding-round'));
});
}
return true;
})();
await this.isSeedingComplete;
next();
}
}
Voici enfin l'entité que nous utilisons pour enregistrer dans notre db qu'un ensemencement d'un certain type n'a eu lieu. Assurez-vous de vous enregistrer en tant qu'entité dans votre TypeOrmModule.forRoot
appel.
// file: src/seeding/entities/Seeding.entity.ts
import { Entity, PrimaryColumn, CreateDateColumn } from 'typeorm';
@Entity()
export class Seeding {
@PrimaryColumn()
public id: string;
@CreateDateColumn()
creationDate: Date;
constructor(id?: string) {
this.id = id;
}
}
Une alternative ensemencement de la solution à l'aide du cycle de vie des événements:
avec Nest.js vous pouvez également mettre en œuvre l' OnApplicationBootstrap
interface (voir les événements de cycle de vie) au lieu d'aller pour un middleware de base de la solution pour gérer vos semis. L' onApplicationBootstrap
méthode "appelé une fois que l'application est complètement démarré et est bootstrap". Cette approche, cependant, contrairement à un middleware-solution, ne vous permettra pas de graines de votre base de données dans un environnement multi-locataire de l'environnement où db-des schémas pour les différents locataires seront créés lors de l'exécution et de semis doit être effectué plusieurs fois au moment de l'exécution pour les différents locataires, après ils sont créés.