npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@cafercangundogdu/nestjs-zod-config

v0.1.0

Published

Type-safe, decorator-free NestJS configuration with Zod validation and plain constructor injection

Downloads

618

Readme

@cafercangundogdu/nestjs-zod-config

npm version npm downloads CI License: MIT TypeScript NestJS Zod

Type-safe, decorator-free NestJS configuration with Zod validation and plain constructor injection.

Why?

NestJS configuration typically requires @Inject() decorators, string-based keys, or manual ConfigService.get() calls — all of which lose type safety or add boilerplate. This library gives you:

  • Plain constructor injection — no @Inject(), no string keys
  • Zod validation at startup — fail fast with clear error messages
  • Full type safety — missing properties, wrong types, and env var typos caught at compile time
  • Zero global state — pure defineConfig(), explicit forRoot({ configs })
  • Dual ESM/CJS package — native import and require entry points, validated with publint and arethetypeswrong
@Injectable()
export class UserService {
  constructor(private auth: AuthConfig) {} // fully typed, no decorators

  getSecret() {
    return this.auth.jwtSecret; // string - autocomplete works
  }
}

Install

pnpm add @cafercangundogdu/nestjs-zod-config

Peer dependencies:

pnpm add @nestjs/common @nestjs/config zod reflect-metadata

Quick start

1. Define a config

// config/auth.config.ts
import { z } from 'zod';
import { defineConfig } from '@cafercangundogdu/nestjs-zod-config';

export abstract class AuthConfig {
  abstract jwtSecret: string;
  abstract jwtExpiresIn: string;
  abstract bcryptRounds: number;
}

export const auth = defineConfig({
  name: 'auth',
  token: AuthConfig,
  schema: z.object({
    JWT_SECRET: z.string().min(1),
    JWT_EXPIRES_IN: z.string().default('7d'),
    BCRYPT_ROUNDS: z.coerce.number().default(10),
  }),
  map: (env) => ({
    jwtSecret: env.JWT_SECRET,
    jwtExpiresIn: env.JWT_EXPIRES_IN,
    bcryptRounds: env.BCRYPT_ROUNDS,
  }),
});

2. Register configs in your module

// app.module.ts
import { Module } from '@nestjs/common';
import { TypedConfigModule } from '@cafercangundogdu/nestjs-zod-config';
import { auth } from './config/auth.config';
import { database } from './config/database.config';

@Module({
  imports: [
    TypedConfigModule.forRoot({
      configs: [auth, database],
    }),
  ],
})
export class AppModule {}

3. Inject with plain constructor injection

// user.service.ts
import { Injectable } from '@nestjs/common';
import { AuthConfig } from './config/auth.config';
import { DatabaseConfig } from './config/database.config';

@Injectable()
export class UserService {
  constructor(
    private auth: AuthConfig,
    private db: DatabaseConfig,
  ) {}

  getTokenExpiry() {
    return this.auth.jwtExpiresIn; // string
  }

  getPoolSize() {
    return this.db.poolSize; // number
  }
}

No @Inject(), no string keys, no decorators. The abstract class serves as both the TypeScript type and the NestJS injection token.

API

defineConfig(options)

Pure function with no side effects. Returns a ConfigDefinition to pass to TypedConfigModule.forRoot().

| Option | Type | Description | | -------- | ---------------- | ---------------------------------------------------- | | name | string | Unique namespace name for @nestjs/config internals | | token | abstract class | Abstract class used as both DI token and type | | schema | z.ZodObject | Zod schema for env var validation | | map | (parsed) => T | Maps validated env vars to config shape |

TypedConfigModule.forRoot(options)

| Option | Type | Default | Description | | ----------------- | -------------------- | ------- | ------------------------------- | | configs | ConfigDefinition[] | - | Config definitions to register | | isGlobal | boolean | true | Register as global module | | envFilePath | string \| string[] | - | Path to .env file(s) | | expandVariables | boolean | - | Expand $VAR references in env |

Duplicate namespace names or tokens are detected at startup and throw immediately.

How it works

  1. defineConfig() creates a Zod schema + NestJS provider pair (pure, no global state)
  2. TypedConfigModule.forRoot({ configs }) receives the definitions explicitly and:
    • Checks for duplicate namespaces/tokens
    • Validates only the given schemas against process.env at startup
    • Passes namespaces to ConfigModule.forRoot({ load: [...] })
    • Registers providers that bridge @nestjs/config namespaces to abstract class tokens
  3. At injection time, NestJS resolves the abstract class token to the validated, mapped config object

Type safety

The generic chain is fully connected with compile-time checks:

| Scenario | Caught at | Mechanism | | ------------------------------- | ----------- | -------------------------- | | Missing property in map | Compile | NoInfer<TConfig> | | Wrong property type in map | Compile | NoInfer<TConfig> | | Typo in env var name | Compile | z.infer<ZodObject> | | Invalid env var value | Startup | Zod validation | | Missing required env var | Startup | Zod validation | | Duplicate namespace/token | Startup | checkDuplicates() | | Wrong property access on inject | Compile | Abstract class type |

Compatibility

| Dependency | Supported versions | | ------------------- | ------------------ | | @nestjs/common | ^10.0.0 || ^11.0.0 | | @nestjs/config | ^3.0.0 || ^4.0.0 | | zod | ^3.20.0 || ^4.0.0 | | reflect-metadata | ^0.1.13 || ^0.2.0 | | typescript | >= 5.4 (requires NoInfer) |

License

MIT