{"_id":"@alireza_ghasemi/env-typecheck","name":"@alireza_ghasemi/env-typecheck","dist-tags":{"latest":"1.0.0"},"versions":{"1.0.0":{"name":"@alireza_ghasemi/env-typecheck","version":"1.0.0","description":"Type-safe environment variables with Zod — for Express, NestJS and any TypeScript project","author":{"name":"Alireza-ghasemi"},"license":"MIT","main":"dist/index.js","module":"dist/index.mjs","types":"dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.mjs","require":"./dist/index.js"},"./nestjs":{"types":"./dist/nestjs/index.d.ts","import":"./dist/nestjs/index.mjs","require":"./dist/nestjs/index.js"}},"scripts":{"build":"tsup src/index.ts src/nestjs/index.ts --format cjs,esm --dts --clean","typecheck":"tsc --noEmit","test":"vitest run","dev":"tsup --watch"},"dependencies":{"dotenv":"^16.0.0","zod":"^3.22.0"},"peerDependencies":{"@nestjs/common":">=9.0.0"},"peerDependenciesMeta":{"@nestjs/common":{"optional":true}},"devDependencies":{"@nestjs/common":"^11.1.26","@types/node":"^20.0.0","reflect-metadata":"^0.1.13","tsup":"^8.0.0","typescript":"^5.0.0","vitest":"^4.1.8"},"keywords":["env","environment","zod","nestjs","express","typescript","type-safe","validation","config"],"_id":"@alireza_ghasemi/env-typecheck@1.0.0","gitHead":"cda9eb8532c49ce80530bd6f327eecedceaf975a","_nodeVersion":"22.15.0","_npmVersion":"10.9.2","dist":{"integrity":"sha512-IBLqNWqii4CWk2Qt0oH2vLRQpyAAra/6616y0S3NxF7s6T+teIU0A35tNkt9MCBZTSdJz/ao3BvimC9LwDtbxQ==","shasum":"f51a544cdd14fe854bdaffae82744a3fc07d50d7","tarball":"https://registry.npmjs.org/@alireza_ghasemi/env-typecheck/-/env-typecheck-1.0.0.tgz","fileCount":25,"unpackedSize":38494,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQCLw+143wgq1NPsJ9TW0nbMMY/AIumkMQgLKdewHtlgFgIhAP5BVnpAk2Bj71B/uHrP+SG5tObmOtSTuRSg3aK4CYzm"}]},"_npmUser":{"name":"alireza_ghasemi","email":"alirezag960@gmail.com"},"directories":{},"maintainers":[{"name":"alireza_ghasemi","email":"alirezag960@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/env-typecheck_1.0.0_1780925304326_0.6257259871051226"},"_hasShrinkwrap":false}},"time":{"created":"2026-06-08T13:28:24.095Z","1.0.0":"2026-06-08T13:28:24.462Z","modified":"2026-06-08T13:28:24.654Z"},"maintainers":[{"name":"alireza_ghasemi","email":"alirezag960@gmail.com"}],"description":"Type-safe environment variables with Zod — for Express, NestJS and any TypeScript project","keywords":["env","environment","zod","nestjs","express","typescript","type-safe","validation","config"],"author":{"name":"Alireza-ghasemi"},"license":"MIT","readme":"# env-typecheck\n\nType-safe environment variable validation for TypeScript projects, powered by\n[Zod](https://zod.dev/) and [dotenv](https://github.com/motdotla/dotenv).\n\nUse it in Express, NestJS, CLIs, workers, queues, scripts, or any Node.js app\nthat needs validated configuration at startup.\n\n## Why env-typecheck?\n\nEnvironment variables are always strings at runtime, but applications usually\nneed numbers, booleans, URLs, enums, optional values, defaults, and clear startup\nerrors. `env-typecheck` lets you describe your environment once with Zod and then\nuse the parsed result with full TypeScript inference.\n\nBenefits:\n\n- Type-safe env access from your Zod schema.\n- Runtime validation before the app starts serving traffic.\n- Automatic coercion through Zod, such as `\"3000\"` to `3000`.\n- Helpful formatted error messages for missing or invalid variables.\n- Works with one `.env` file or multiple `.env` files.\n- Lightweight core API for Express and plain TypeScript projects.\n- Optional NestJS module with dependency injection support.\n- ESM and CommonJS builds.\n- Sensitive values are redacted in NestJS validation logs by default.\n\n## Installation\n\n```bash\nnpm install @alireza_ghasemi/env-typecheck zod dotenv\n```\n\n`zod` and `dotenv` are regular dependencies of this package, so installing\n`@alireza_ghasemi/env-typecheck` is enough for most npm setups. They are shown above because most\nprojects also import `z` directly in their own schema files.\n\nFor NestJS usage, your application should already have Nest installed:\n\n```bash\nnpm install @nestjs/common @nestjs/core reflect-metadata rxjs\n```\n\n## Quick Start\n\n```ts\nimport { createEnv, z } from \"@alireza_ghasemi/env-typecheck\";\n\nexport const env = createEnv(\n  z.object({\n    NODE_ENV: z.enum([\"development\", \"test\", \"production\"]).default(\"development\"),\n    PORT: z.coerce.number().int().positive().default(3000),\n    DATABASE_URL: z.string().url(),\n    ENABLE_JOBS: z.coerce.boolean().default(false),\n  }),\n);\n\nenv.PORT; // number\nenv.DATABASE_URL; // string\nenv.ENABLE_JOBS; // boolean\n```\n\nIf validation fails, the process exits by default with a readable error message.\n\n```txt\n[env-typecheck] Invalid environment variables:\n  - DATABASE_URL: Invalid url\n```\n\n## Recommended Project Setup\n\nCreate a small env file and import it wherever configuration is needed.\n\n```ts\n// src/env.ts\nimport { createEnv, z } from \"@alireza_ghasemi/env-typecheck\";\n\nexport const envSchema = z.object({\n  NODE_ENV: z.enum([\"development\", \"test\", \"production\"]).default(\"development\"),\n  PORT: z.coerce.number().default(3000),\n  DATABASE_URL: z.string().url(),\n  JWT_SECRET: z.string().min(32),\n});\n\nexport const env = createEnv(envSchema);\nexport type Env = z.infer<typeof envSchema>;\n```\n\nThen use the parsed config:\n\n```ts\n// src/index.ts\nimport { env } from \"./env\";\n\nconsole.log(`Starting server on port ${env.PORT}`);\n```\n\n## Express Usage\n\nSee the runnable example in [`examples/express`](examples/express).\n\n```ts\nimport express from \"express\";\nimport { createEnv, z } from \"@alireza_ghasemi/env-typecheck\";\n\nconst env = createEnv(\n  z.object({\n    NODE_ENV: z.enum([\"development\", \"test\", \"production\"]).default(\"development\"),\n    PORT: z.coerce.number().int().positive().default(3000),\n    DATABASE_URL: z.string().url(),\n    CORS_ORIGIN: z.string().url().optional(),\n  }),\n);\n\nconst app = express();\n\napp.get(\"/health\", (_req, res) => {\n  res.json({\n    ok: true,\n    env: env.NODE_ENV,\n  });\n});\n\napp.listen(env.PORT, () => {\n  console.log(`Server is running on port ${env.PORT}`);\n});\n```\n\nBecause `env.PORT` is inferred as a `number`, you do not need to manually parse\nit later in your application.\n\n## NestJS Usage\n\nNestJS support is exported from the `@alireza_ghasemi/env-typecheck/nestjs` subpath.\nSee the runnable example in [`examples/nestjs`](examples/nestjs).\n\n```ts\n// src/env.schema.ts\nimport { z } from \"@alireza_ghasemi/env-typecheck\";\n\nexport const envSchema = z.object({\n  NODE_ENV: z.enum([\"development\", \"test\", \"production\"]).default(\"development\"),\n  PORT: z.coerce.number().int().positive().default(3000),\n  DATABASE_URL: z.string().url(),\n  JWT_SECRET: z.string().min(32),\n});\n```\n\nRegister the module in your root module:\n\n```ts\n// src/app.module.ts\nimport { Module } from \"@nestjs/common\";\nimport { EnvModule } from \"@alireza_ghasemi/env-typecheck/nestjs\";\nimport { envSchema } from \"./env.schema\";\n\n@Module({\n  imports: [\n    EnvModule.forRoot({\n      isGlobal: true,\n      schema: envSchema,\n      envFilePath: [\".env.local\", \".env\"],\n    }),\n  ],\n})\nexport class AppModule {}\n```\n\nInject the typed service:\n\n```ts\n// src/app.service.ts\nimport { Injectable } from \"@nestjs/common\";\nimport { EnvService } from \"@alireza_ghasemi/env-typecheck/nestjs\";\nimport { envSchema } from \"./env.schema\";\n\n@Injectable()\nexport class AppService {\n  constructor(private readonly env: EnvService<typeof envSchema>) {}\n\n  getDatabaseUrl() {\n    return this.env.get(\"DATABASE_URL\");\n  }\n\n  getPort() {\n    return this.env.get(\"PORT\");\n  }\n}\n```\n\nUseful `EnvService` helpers:\n\n```ts\nthis.env.get(\"PORT\"); // typed value\nthis.env.getOrDefault(\"PORT\", 3000); // fallback only when value is undefined\nthis.env.getAll(); // shallow copy of the parsed config\nthis.env.isDevelopment; // NODE_ENV === \"development\"\nthis.env.isProduction; // NODE_ENV === \"production\"\nthis.env.isTest; // NODE_ENV === \"test\"\n```\n\n### NestJS Async Registration\n\nUse `forRootAsync` when the env file path depends on another provider or an async\noperation.\n\n```ts\nimport { Module } from \"@nestjs/common\";\nimport { EnvModule } from \"@alireza_ghasemi/env-typecheck/nestjs\";\nimport { envSchema } from \"./env.schema\";\n\n@Module({\n  imports: [\n    EnvModule.forRootAsync({\n      isGlobal: true,\n      schema: envSchema,\n      useFactory: async () => {\n        return {\n          envFilePath:\n            process.env.NODE_ENV === \"production\" ? \".env.production\" : \".env\",\n        };\n      },\n    }),\n  ],\n})\nexport class AppModule {}\n```\n\n### Injecting the Raw Config in NestJS\n\nYou can also inject the parsed object directly.\n\n```ts\nimport { Inject, Injectable } from \"@nestjs/common\";\nimport { ENV_CONFIG, EnvConfig } from \"@alireza_ghasemi/env-typecheck/nestjs\";\nimport { envSchema } from \"./env.schema\";\n\n@Injectable()\nexport class DatabaseService {\n  constructor(\n    @Inject(ENV_CONFIG)\n    private readonly env: EnvConfig<typeof envSchema>,\n  ) {}\n}\n```\n\n## API\n\n### `createEnv(schema, options?)`\n\nValidates `process.env` with a Zod object schema and returns the parsed result.\n\n```ts\nconst env = createEnv(schema, {\n  path: \".env\",\n  exitOnError: true,\n});\n```\n\nOptions:\n\n| Option | Type | Default | Description |\n| --- | --- | --- | --- |\n| `path` | `string | string[]` | `\".env\"` | One or more dotenv files to load before validation. |\n| `exitOnError` | `boolean` | `true` | When `true`, validation errors are printed and the process exits with code `1`. When `false`, an error is thrown. |\n\n### `EnvModule.forRoot(options)`\n\nRegisters the NestJS module synchronously.\n\n| Option | Type | Default | Description |\n| --- | --- | --- | --- |\n| `schema` | `ZodObject` | required | Zod schema used to validate `process.env`. |\n| `envFilePath` | `string | string[]` | `\".env\"` | One or more dotenv files to load. |\n| `isGlobal` | `boolean` | `false` | Makes the module global in NestJS. |\n| `onError` | `\"exit\" | \"throw\"` | `\"exit\"` | Controls validation failure behavior. |\n| `redactKeys` | `string[]` | `[\"PASSWORD\", \"SECRET\", \"KEY\", \"TOKEN\", \"PRIVATE\"]` | Key patterns hidden in success logs. |\n\n### `EnvModule.forRootAsync(options)`\n\nRegisters the NestJS module with an async factory.\n\n```ts\nEnvModule.forRootAsync({\n  schema,\n  inject: [],\n  useFactory: async () => ({\n    envFilePath: \".env\",\n  }),\n});\n```\n\n### `EnvService<TSchema>`\n\nNestJS injectable wrapper around the parsed config.\n\n| Method / getter | Description |\n| --- | --- |\n| `get(key)` | Returns a typed value from the parsed config. |\n| `getOrDefault(key, defaultValue)` | Returns the value or a fallback when the value is `undefined`. |\n| `getAll()` | Returns a shallow copy of all parsed config values. |\n| `isDevelopment` | `true` when `NODE_ENV` is `\"development\"`. |\n| `isProduction` | `true` when `NODE_ENV` is `\"production\"`. |\n| `isTest` | `true` when `NODE_ENV` is `\"test\"`. |\n\n## Multiple `.env` Files\n\nBoth the core API and NestJS module support multiple dotenv files.\n\n```ts\nconst env = createEnv(schema, {\n  path: [\".env.local\", \".env\"],\n});\n```\n\n```ts\nEnvModule.forRoot({\n  schema,\n  envFilePath: [\".env.local\", \".env\"],\n});\n```\n\nFiles are loaded in the order you provide. Existing environment variables are\nnot overwritten because dotenv is loaded with `override: false`.\n\n## Type Inference\n\nTypes come directly from the schema.\n\n```ts\nconst schema = z.object({\n  PORT: z.coerce.number(),\n  NODE_ENV: z.enum([\"development\", \"production\"]),\n});\n\nconst env = createEnv(schema);\n\nenv.PORT.toFixed(0); // OK, PORT is number\nenv.NODE_ENV; // \"development\" | \"production\"\n```\n\n## Common Patterns\n\n### Required Secret\n\n```ts\nJWT_SECRET: z.string().min(32)\n```\n\n### Optional Value\n\n```ts\nSENTRY_DSN: z.string().url().optional()\n```\n\n### Default Number\n\n```ts\nPORT: z.coerce.number().int().positive().default(3000)\n```\n\n### Boolean Flag\n\n```ts\nENABLE_JOBS: z.coerce.boolean().default(false)\n```\n\n### Comma-separated List\n\n```ts\nALLOWED_ORIGINS: z\n  .string()\n  .transform((value) => value.split(\",\").map((item) => item.trim()))\n  .default(\"http://localhost:3000\")\n```\n\n## Security Notes\n\n- Do not commit real `.env` files.\n- Keep `.env.example` in your repository with safe placeholder values.\n- Validate secrets at startup so broken deployments fail early.\n- In NestJS, validation success logs redact keys containing `PASSWORD`,\n  `SECRET`, `KEY`, `TOKEN`, or `PRIVATE` by default.\n- Customize `redactKeys` if your project uses different secret naming.\n\n## Publishing Checklist\n\nBefore publishing a new version:\n\n```bash\nnpm run typecheck\nnpm test\nnpm run build\nnpm audit\nnpm pack --dry-run\n```\n\nThe package exports:\n\n```ts\nimport { createEnv, z } from \"@alireza_ghasemi/env-typecheck\";\nimport { EnvModule, EnvService } from \"@alireza_ghasemi/env-typecheck/nestjs\";\n```\n\n## License\n\nMIT\n","readmeFilename":"README.md","_rev":"1-537dd1ce5bd871672cc7e2c128c4fcaa"}