CtroEnv
ctroenvType-Safe Environment Variables
Getting StartedQuick StartCore Concepts
defineEnv()string()number()boolean()pick()Chainable MethodsRefinementsError HandlingSchema Composition
CLI Overviewctroenv validatectroenv generatectroenv checkctroenv docsctroenv initCLI Configuration
Node AdapterVite AdapterNext.js Adapter
Migration from t3-envMigration from envalidMigration from dotenv

Migration from dotenv

Migrate from plain dotenv to CtroEnv for type safety and validation.

  1. Docs
  2. Migration

Migration from dotenv

If you're using dotenv with raw process.env access, CtroEnv adds validation, type safety, and structured error handling.

Key Differences

FeaturedotenvCtroEnv
ValidationNoneFull validation with type coercion
TypeScriptstring | undefinedExact inferred types
Error messagesNoneRich, grouped, with suggestions
Default valuesManualBuilt-in .default()
Secret handlingNone.secret() masking
.env loadingManual@ctroenv/node adapter

Step-by-Step Migration

1. Install CtroEnv

npm install @ctroenv/core @ctroenv/node

2. Replace dotenv usage

// Before (dotenv)
import "dotenv/config"

const dbUrl = process.env.DATABASE_URL
if (!dbUrl) {
  throw new Error("DATABASE_URL is required")
}
const port = Number(process.env.PORT) || 3000

// After (CtroEnv)
import { defineEnv, string, number } from "@ctroenv/core"
import { loadEnv } from "@ctroenv/node"

const env = defineEnv(
  {
    DATABASE_URL: string().url(),
    PORT: number().port().default(3000),
  },
  { source: loadEnv() },
)

3. Remove manual validation

// Before: manual checks everywhere
function getConfig() {
  const nodeEnv = process.env.NODE_ENV
  if (!["dev", "prod"].includes(nodeEnv ?? "")) {
    throw new Error("Invalid NODE_ENV")
  }
  return { nodeEnv }
}

// After: single schema definition
const env = defineEnv({
  NODE_ENV: pick(["dev", "prod"]),
})

What You Gain

Type Safety

// Before: process.env.PORT is string | undefined
const port = process.env.PORT  // string | undefined

// After: env.PORT is number (or throws at init)
const port = env.PORT  // number

Early Failure

// Before: errors surface when the value is first accessed
function handleRequest() {
  const db = process.env.DATABASE_URL  // May fail deep in request handling
}

// After: errors surface at startup
const env = defineEnv({ ... })  // Fails immediately if anything is wrong

Single Source of Truth

// Before: validation scattered across the codebase
// After: all env vars defined and validated in one place

How is this guide?

Edit on GitHub

Last updated on Jun 24, 2026

PreviousMigration from envalid

On this page

Key DifferencesStep-by-Step Migration1. Install CtroEnv2. Replace dotenv usage3. Remove manual validationWhat You GainType SafetyEarly FailureSingle Source of Truth