guide default admin pattern - wwwsolutions/nestjs-starter-kit GitHub Wiki

Feature | Business logic

Example from Prisma-GraphQL integration

Create default admin functionality

This is a common pattern. Upon initialization application loads default admin from environment and saves it into database.

Create admin configuration

ADD ENV VARIABLES
# apps/api/.env.development

##### ADMIN CONFIGURATION ################################################################################

### REQUIRED

ADMIN_EMAIL='[email protected]'
ADMIN_PASSWORD='password'
ADD VALIDATION
# libs/api/config/features/src/lib/validation.schema.ts

  /* --------------------------------------------------------------
  ADMIN
  api/config/features/src/lib/configs/admin.configuration.ts
  --------------------------------------------------------------- */

  // REQUIRED
  ADMIN_EMAIL: Joi.string()
    .email()
    .required()
    .description('API default admin email'),
  ADMIN_PASSWORD: Joi.string()
    .required()
    .description('API default admin password'),
CREATE CONFIGURATION FILE
// libs/api/config/features/src/lib/configs/admin.configuration.ts

import { Inject } from '@nestjs/common';
import { ConfigType, registerAs } from '@nestjs/config';

import { AuthenticationUtils } from '@wwwsolutions/shared/utils';

import { UserCreateInput } from '@wwwsolutions/api/data-access/models';

export const adminConfiguration = registerAs('admin', async () => ({
  admin: {
    email: process.env.ADMIN_EMAIL as string,
    password: await AuthenticationUtils.hash(
      process.env.ADMIN_PASSWORD as string
    ),

    get defaultAdmin(): UserCreateInput {
      return {
        email: this.email,
        password: this.password,
      };
    },
  },
}));

export type AdminConfiguration = ConfigType<typeof adminConfiguration>;

export const InjectAdminConfig = () => Inject(adminConfiguration.KEY);
REGISTER CONFIGURATION
// libs/api/config/features/src/lib/registrations/business-logic-configs.registration.ts

import { adminConfiguration } from '../configs/admin.configuration';

export const businessLogicConfigs: any = [adminConfiguration];
EXPOSE
// libs/api/config/features/src/index.ts

export * from './lib/configs/admin.configuration';

Implement default admin

CONSUME CONFIGURATION
// libs/api/data-access/core/src/lib/prisma-data.service.ts

...
import {
  InjectAdminConfig,
  AdminConfiguration,
} from '@wwwsolutions/api/config/features';
...

@Injectable()
export class PrismaDataService
  extends PrismaClient
  implements OnModuleInit, OnModuleDestroy
{
  ...
  private readonly defaultAdmin: UserCreateInput | undefined;
  ...

  constructor(
    ...
     @InjectAdminConfig()
     private readonly adminConfiguration: AdminConfiguration
    ...
  ) {
    ...
    this.defaultAdmin = this.adminConfiguration.admin.defaultAdmin;

    this.logger.log(
      `🔶 Load default admin user from .env via adminConfiguration, '${this.defaultAdmin.email}'`
    );
    ...
  }
}
CREATE HELPER METHOD
// libs/api/data-access/core/src/lib/prisma-data.service.ts

@Injectable()
export class PrismaDataService
  extends PrismaClient
  implements OnModuleInit, OnModuleDestroy
{

  ...
  async onModuleInit(): Promise<void> {
    ...
    await this.ensureDefaultAdminUser();
  }
  ...

  ...
  // [HELPER METHODS]
  /**
   * Ensures that default admin user exists in database,
   * if not, it creates one from from data stored in .env
   */
  private async ensureDefaultAdminUser(): Promise<boolean> {
    // Check if default admin user exists in database
    const found = await this.findUserByEmail(this.defaultAdmin!.email);

    if (found) {
      this.logger.log(`🤓 Existing default admin user: '${found.email}'`);
      return true;
    }

    // Default admin user does not exist in database, create it
    const created = await this.createUser({
      data: this.defaultAdmin!,
    });

    this.logger.log(`🤓 Created default admin user: '${created.email}'`);

    return true;
  }
  ...
}
⚠️ **GitHub.com Fallback** ⚠️