Skip to content

feat: add Zod environment validation#2

Open
aabrius wants to merge 2 commits into
masterfrom
feature/config-validation
Open

feat: add Zod environment validation#2
aabrius wants to merge 2 commits into
masterfrom
feature/config-validation

Conversation

@aabrius

@aabrius aabrius commented Dec 7, 2025

Copy link
Copy Markdown
Contributor

Summary

  • Add Zod-based environment variable validation with type coercion
  • Replace runtime checks with compile-time schema validation
  • Add comprehensive tests (37 tests)

Test plan

  • Unit tests for Zod schema validation
  • Tests for type coercion (PORT string to number)
  • Tests for ALLOWED_ORIGINS transform
  • Tests for required field validation

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Chores

    • Updated backend environment configuration with integrated validation
    • Reorganized authentication settings with new FRONTEND_URL and ALLOWED_ORIGINS configuration options
    • Added dependency to strengthen configuration validation
  • Tests

    • Added comprehensive test suite for environment configuration validation

✏️ Tip: You can customize this high-level summary in your review settings.

aabrius and others added 2 commits December 6, 2025 22:02
- Add src/config/env.validation.ts with typed Zod schema
- Validate all env vars at app startup with clear error messages
- Update ConfigModule to use validation and make it global
- Update .env.example with all future env vars documented
- Add zod dependency

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
- Test valid configurations with required and optional fields
- Test default values for all fields with defaults
- Test type coercion (PORT, REDIS_PORT string to number)
- Test transforms (TYPEORM_LOGGING boolean, ALLOWED_ORIGINS array)
- Test validation errors for missing/invalid required fields
- Test NODE_ENV enum values
- Test validateEnv error formatting

37 tests covering all Zod schema behavior

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Dec 7, 2025

Copy link
Copy Markdown

Walkthrough

This PR introduces environment validation infrastructure for the backend application. It adds a Zod-based validation schema for configuration variables, integrates validation into the application startup, updates the example environment file with new configuration entries, adds the zod dependency, and includes a comprehensive test suite.

Changes

Cohort / File(s) Summary
Environment Validation Implementation
apps/backend/src/config/env.validation.ts, apps/backend/src/config/env.validation.spec.ts, apps/backend/src/config/index.ts
Introduces Zod schema for comprehensive environment validation with defaults, type coercions, and transformations; adds test suite covering valid/invalid configs, defaults, type coercion, and error aggregation; exports validation module.
Application Configuration
apps/backend/src/app.module.ts
Updates ConfigModule to use validateEnv function and set global scope.
Dependencies & Configuration
apps/backend/package.json, apps/backend/.env.example
Adds zod ^4.1.13 dependency; removes Auth0-specific variables (IDP_ISSUER, AUTH0_DOMAIN, AUTH0_CLIENT_ID, AUTH0_CLIENT_SECRET, AUTH0_ROLES_NAME, AUTH0_CONNECTION_ID); adds FRONTEND_URL and ALLOWED_ORIGINS; consolidates and reorders configuration sections.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

  • Validation schema logic: Verify all required/optional fields, defaults, type coercions, and transformation rules (ALLOWED_ORIGINS string-to-array, TYPEORM_LOGGING string-to-boolean)
  • Test coverage: Ensure tests adequately cover edge cases, error aggregation, and type inference
  • Configuration changes: Confirm removal of Auth0 variables aligns with application needs and that new FRONTEND_URL/ALLOWED_ORIGINS integration points are correct

Suggested reviewers

  • filipecrosk

Poem

🐰 With Zod's strong hand and schemas bright,
Our config now validates just right,
No silent fails, no guesses here—
Each variable rings loud and clear!
The startup flow flows pure and clean,

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: add Zod environment validation' clearly and concisely summarizes the main change—adding Zod-based environment validation—which is the primary focus of this pull request across all modified files.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feature/config-validation

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (4)
apps/backend/src/config/index.ts (1)

1-1: Good use of barrel export for config utilities

Re-exporting from ./env.validation via index.ts matches the barrel-export convention and keeps config APIs centralized.

Based on learnings, this pattern is exactly what we want for config modules.

apps/backend/.env.example (1)

1-43: Example env matches schema; consider reordering keys to satisfy dotenv-linter

The example file now exposes all keys required by envSchema (including FRONTEND_URL and ALLOWED_ORIGINS), so it should pass validation out of the box, which is great.

Static analysis only flags key ordering (e.g., IDP_ISSUER/IDP_AUDIENCE/AUTH0_* and REDIS_*). If dotenv-linter runs in CI, you may want to reorder those keys to clear the warnings; otherwise this is purely cosmetic.

apps/backend/src/config/env.validation.spec.ts (1)

1-323: Comprehensive env validation tests; only minor polish possible

The suite does a good job exercising defaults, coercions, transforms, error aggregation, and type inference for EnvConfig. This should give high confidence in the Zod schema and validateEnv.

The only tiny nit is in the "should throw Error with formatted message" test, where validateEnv(invalidConfig) is invoked twice in separate expect calls; you could assign a const call = () => validateEnv(invalidConfig); and reuse it to avoid duplicate work, but this is purely cosmetic.

apps/backend/src/config/env.validation.ts (1)

1-76: Schema and validator design are strong; consider tightening types and a small edge case

The Zod schema is well-structured (defaults, coercions, transforms) and lines up with both .env.example and the tests. validateEnv’s aggregated error message with field paths is also very helpful for debugging.

Two small refinements you might consider:

  1. Return EnvConfig from validateEnv
    Since you already export EnvConfig = z.infer<typeof envSchema>, you could change the signature to:

    export function validateEnv(config: Record<string, unknown>): EnvConfig {
      const result = envSchema.safeParse(config);
      // ...
      return result.data;
    }

    This gives you end‑to‑end type safety wherever validateEnv is consumed.

  2. Optional ALLOWED_ORIGINS edge case
    With .transform((s) => s.split(',').map((origin) => origin.trim())), setting ALLOWED_ORIGINS to an empty string would yield ['']. If you want to avoid that, you could filter out empty entries or special‑case '*' vs non‑'*' values.

These are polish-level improvements; behavior is already correct for the tested scenarios.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 793cb77 and ca001b9.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (6)
  • apps/backend/.env.example (1 hunks)
  • apps/backend/package.json (1 hunks)
  • apps/backend/src/app.module.ts (1 hunks)
  • apps/backend/src/config/env.validation.spec.ts (1 hunks)
  • apps/backend/src/config/env.validation.ts (1 hunks)
  • apps/backend/src/config/index.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (10)
apps/backend/src/**/*.spec.ts

📄 CodeRabbit inference engine (CLAUDE.md)

Include unit tests for all backend services using Jest

Files:

  • apps/backend/src/config/env.validation.spec.ts
**/*.{js,ts,tsx,jsx,css,scss,json,yaml,yml,md}

📄 CodeRabbit inference engine (.cursor/rules/cursor.json)

**/*.{js,ts,tsx,jsx,css,scss,json,yaml,yml,md}: Maximum line length should not exceed 100 characters
Trim trailing whitespace from all lines
Insert a final newline at the end of every file

Files:

  • apps/backend/src/config/env.validation.spec.ts
  • apps/backend/package.json
  • apps/backend/src/config/env.validation.ts
  • apps/backend/src/config/index.ts
  • apps/backend/src/app.module.ts
**/*.{js,ts,tsx,jsx,css,scss,json,yaml,yml}

📄 CodeRabbit inference engine (.cursor/rules/cursor.json)

Use 2-space indentation for all files

Files:

  • apps/backend/src/config/env.validation.spec.ts
  • apps/backend/package.json
  • apps/backend/src/config/env.validation.ts
  • apps/backend/src/config/index.ts
  • apps/backend/src/app.module.ts
**/*

📄 CodeRabbit inference engine (.cursor/rules/cursor.json)

**/*: Use LF (line feed) as end-of-line character
Use UTF-8 character encoding for all files

Files:

  • apps/backend/src/config/env.validation.spec.ts
  • apps/backend/package.json
  • apps/backend/src/config/env.validation.ts
  • apps/backend/src/config/index.ts
  • apps/backend/src/app.module.ts
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.cursor/rules/cursor.json)

**/*.{ts,tsx,js,jsx}: Use single quotes for string literals in TypeScript/JavaScript
Semicolons must be present at the end of statements in TypeScript/JavaScript
Use trailing commas in multiline constructs following ES5 convention
Enable bracket spacing in object literals (e.g., { foo: bar } instead of {foo: bar})
Omit parentheses around single arrow function parameters when possible

Files:

  • apps/backend/src/config/env.validation.spec.ts
  • apps/backend/src/config/env.validation.ts
  • apps/backend/src/config/index.ts
  • apps/backend/src/app.module.ts
**/*.{ts,tsx,js,jsx,vue}

📄 CodeRabbit inference engine (.cursor/rules/etus-methodology.mdc)

**/*.{ts,tsx,js,jsx,vue}: All UI component imports must use @/components/ui/* from @ETUS Design System, fail with error if components are imported from outside this path
Do not create components outside @ETUS Design System

Files:

  • apps/backend/src/config/env.validation.spec.ts
  • apps/backend/src/config/env.validation.ts
  • apps/backend/src/config/index.ts
  • apps/backend/src/app.module.ts
apps/backend/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/project-context.mdc)

All operations must include account context (accountId) for account isolation

Files:

  • apps/backend/src/config/env.validation.spec.ts
  • apps/backend/src/config/env.validation.ts
  • apps/backend/src/config/index.ts
  • apps/backend/src/app.module.ts
apps/**/*.{vue,ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/project-context.mdc)

Implement comprehensive error handling scenarios in Vue components and backend services

Files:

  • apps/backend/src/config/env.validation.spec.ts
  • apps/backend/src/config/env.validation.ts
  • apps/backend/src/config/index.ts
  • apps/backend/src/app.module.ts
**/*.{ts,tsx,d.ts}

📄 CodeRabbit inference engine (.cursor/rules/typescript.mdc)

**/*.{ts,tsx,d.ts}: Prefer interfaces over types for object definitions
Use type for unions, intersections, and mapped types
Avoid using any, prefer unknown for unknown types
Leverage TypeScript's built-in utility types
Use generics for reusable type patterns
Use PascalCase for type names and interfaces
Use camelCase for variables and functions
Use UPPER_CASE for constants
Use descriptive names with auxiliary verbs for boolean variables and functions (e.g., isLoading, hasError)
Prefix React component prop interfaces with 'Props' (e.g., ButtonProps)
Keep type definitions close to where they're used
Export types and interfaces from dedicated type files when shared
Use explicit return types for public functions
Use arrow functions for callbacks and methods
Implement proper error handling with custom error types
Use function overloads for complex type scenarios
Prefer async/await over Promises
Use readonly for immutable properties
Leverage discriminated unions for type safety
Use type guards for runtime type checking
Implement proper null checking
Avoid type assertions unless necessary
Create custom error types for domain-specific errors
Use Result types for operations that can fail
Use try-catch blocks with typed catch clauses
Handle Promise rejections properly

Files:

  • apps/backend/src/config/env.validation.spec.ts
  • apps/backend/src/config/env.validation.ts
  • apps/backend/src/config/index.ts
  • apps/backend/src/app.module.ts
**/index.ts

📄 CodeRabbit inference engine (.cursor/rules/typescript.mdc)

Use barrel exports (index.ts) for organizing exports

Files:

  • apps/backend/src/config/index.ts
🧠 Learnings (7)
📚 Learning: 2025-11-28T18:13:02.933Z
Learnt from: CR
Repo: etusdigital/boilerplate PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T18:13:02.933Z
Learning: Applies to apps/backend/src/**/*.spec.ts : Include unit tests for all backend services using Jest

Applied to files:

  • apps/backend/src/config/env.validation.spec.ts
📚 Learning: 2025-11-28T18:13:02.933Z
Learnt from: CR
Repo: etusdigital/boilerplate PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T18:13:02.933Z
Learning: Applies to apps/backend/src/modules/**/dto/*.dto.ts : Define DTOs with validation decorators in dto/ subdirectory

Applied to files:

  • apps/backend/src/config/env.validation.ts
  • apps/backend/src/app.module.ts
📚 Learning: 2025-11-28T18:13:02.933Z
Learnt from: CR
Repo: etusdigital/boilerplate PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T18:13:02.933Z
Learning: Applies to apps/backend/src/modules/**/*.controller.ts : Implement input validation with DTOs and class-validator in controllers

Applied to files:

  • apps/backend/src/config/env.validation.ts
📚 Learning: 2025-11-28T18:14:14.233Z
Learnt from: CR
Repo: etusdigital/boilerplate PR: 0
File: .cursor/rules/typescript.mdc:0-0
Timestamp: 2025-11-28T18:14:14.233Z
Learning: Applies to **/index.ts : Use barrel exports (index.ts) for organizing exports

Applied to files:

  • apps/backend/src/config/index.ts
📚 Learning: 2025-11-28T18:14:14.233Z
Learnt from: CR
Repo: etusdigital/boilerplate PR: 0
File: .cursor/rules/typescript.mdc:0-0
Timestamp: 2025-11-28T18:14:14.233Z
Learning: Applies to **/*.{ts,tsx,d.ts} : Export types and interfaces from dedicated type files when shared

Applied to files:

  • apps/backend/src/config/index.ts
📚 Learning: 2025-11-28T18:13:02.933Z
Learnt from: CR
Repo: etusdigital/boilerplate PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T18:13:02.933Z
Learning: Applies to apps/backend/src/modules/**/ : Organize backend modules in feature structure: [entity].module.ts, [entity].controller.ts, [entity].service.ts, [entity].spec.ts, dto/

Applied to files:

  • apps/backend/src/app.module.ts
📚 Learning: 2025-11-28T18:13:02.933Z
Learnt from: CR
Repo: etusdigital/boilerplate PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-11-28T18:13:02.933Z
Learning: Applies to apps/backend/src/modules/**/*.controller.ts : Use JwtAuthGuard on all protected controller endpoints

Applied to files:

  • apps/backend/src/app.module.ts
🧬 Code graph analysis (1)
apps/backend/src/app.module.ts (1)
apps/backend/src/config/env.validation.ts (1)
  • validateEnv (59-76)
🪛 dotenv-linter (4.0.0)
apps/backend/.env.example

[warning] 11-11: [UnorderedKey] The IDP_ISSUER key should go before the JWKS_URI key

(UnorderedKey)


[warning] 12-12: [UnorderedKey] The IDP_AUDIENCE key should go before the IDP_ISSUER key

(UnorderedKey)


[warning] 13-13: [UnorderedKey] The AUTH0_DOMAIN key should go before the IDP_AUDIENCE key

(UnorderedKey)


[warning] 14-14: [UnorderedKey] The AUTH0_CLIENT_ID key should go before the AUTH0_DOMAIN key

(UnorderedKey)


[warning] 15-15: [UnorderedKey] The AUTH0_CLIENT_SECRET key should go before the AUTH0_DOMAIN key

(UnorderedKey)


[warning] 16-16: [UnorderedKey] The AUTH0_ROLES_NAME key should go before the IDP_AUDIENCE key

(UnorderedKey)


[warning] 17-17: [UnorderedKey] The AUTH0_CONNECTION_ID key should go before the AUTH0_DOMAIN key

(UnorderedKey)


[warning] 18-18: [UnorderedKey] The AUTH0_CONNECTION_TYPE key should go before the AUTH0_DOMAIN key

(UnorderedKey)


[warning] 37-37: [UnorderedKey] The JWT_EXPIRES_IN key should go before the JWT_ISSUER key

(UnorderedKey)


[warning] 41-41: [UnorderedKey] The REDIS_HOST key should go before the REDIS_URL key

(UnorderedKey)


[warning] 42-42: [UnorderedKey] The REDIS_PORT key should go before the REDIS_URL key

(UnorderedKey)


[warning] 43-43: [UnorderedKey] The REDIS_PASSWORD key should go before the REDIS_PORT key

(UnorderedKey)

🔇 Additional comments (2)
apps/backend/package.json (1)

22-47: Zod dependency wiring looks correct

Adding "zod": "^4.1.13" to backend dependencies cleanly matches the new env validation module; no issues from a package/config perspective.

apps/backend/src/app.module.ts (1)

13-20: Env validation integration into ConfigModule looks solid

Using ConfigModule.forRoot({ validate: validateEnv, isGlobal: true }) cleanly hooks the Zod schema into app bootstrap and centralizes config validation; no issues spotted here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant