Validation & Shutdown — Series 2

Preview — 3 of 10 questions

What does validating inside the factory achieve over one global schema?

javascript
export default registerAs('database', () => {
  const values = {
    url: process.env.DATABASE_URL,
    poolSize: Number(process.env.DB_POOL_SIZE ?? 10),
  };
  const { error } = databaseSchema.validate(values, { abortEarly: false });
  if (error) throw new Error(`Invalid database config: ${error.message}`);
  return values;
});
AIt runs the validation lazily, on first access to the namespace
BThe rule lives next to the code that shapes the values, so coercion and validation stay in step, and each namespace can be validated against exactly what it needs — which matters in a monorepo where several applications share factories but require different subsets of the environment
CIt is the only way to validate values that were produced by a factory rather than read directly
DIt replaces the need for validationSchema in forRoot, which is deprecated

Why is enableImplicitConversion: true essential here?

javascript
class EnvVars {
  @IsEnum(['development', 'test', 'production']) NODE_ENV: string;
  @IsInt() @Min(1) @Max(65535) PORT: number;
  @IsUrl({ require_tld: false }) DATABASE_URL: string;
}

ConfigModule.forRoot({
  validate: (raw: Record<string, unknown>) => {
    const config = plainToInstance(EnvVars, raw, { enableImplicitConversion: true });
    const errors = validateSync(config, { skipMissingProperties: false });
    if (errors.length) throw new Error(errors.toString());
    return config;
  },
});
AIt allows unknown environment variables to pass through untouched
BIt makes validateSync asynchronous, matching the module's initialisation
CIt enables nested object validation for namespaced keys
DEvery environment variable is a string, so @IsInt() on PORT would fail against '3000'; implicit conversion uses the property's declared type to coerce before validation, so PORT becomes a real number and the returned object is correctly typed

The orchestrator's grace period is 30 seconds and draining sometimes takes minutes. What happens, and what should change?

javascript
async onModuleDestroy() {
  await this.queue.drain();      // waits for every in-flight job
}
AThe grace period expires and the process is SIGKILLed mid-drain, so the work is neither finished nor cleanly abandoned — the hook should be bounded by a timeout well under the grace period, doing what it can and logging what it could not
BNest extends the grace period automatically while a hook is pending
CThe hook is skipped entirely, because Nest enforces a five-second limit on lifecycle hooks
DThe orchestrator waits indefinitely as long as the process is making progress

Sign up free to play

Answer all 10 questions (7 more), see explanations for every answer, and track your score.