> ## Documentation Index
> Fetch the complete documentation index at: https://next-safe-env.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Use next-safe-env in a Node.js application

> Validate env vars in any Node.js app using next-safe-env. Numbers coerce from strings; missing required vars exit the process before serving.

`next-safe-env` works equally well outside Next.js. In any Express server, Fastify app, CLI script, or background worker, you get the same validated, fully-typed `env` object with no framework-specific setup. Numeric variables like `PORT` come back as actual numbers, boolean flags are coerced from strings, and the process refuses to start if anything is misconfigured.

<Steps>
  <Step title="Install next-safe-env">
    Add the package to your project. It has zero runtime dependencies.

    ```bash theme={null}
    npm install next-safe-env
    ```
  </Step>

  <Step title="Create config/env.ts">
    Define your schema in a dedicated config file. Set `adapter: 'node'` explicitly, or omit it and rely on auto-detection - when `NEXT_RUNTIME` is not set and no `NEXT_PUBLIC_` keys are present, `next-safe-env` selects the `node` adapter automatically.

    ```typescript theme={null}
    // config/env.ts
    import { createEnv, str, bool, url, port } from 'next-safe-env'

    export const env = createEnv({
      server: {
        DATABASE_URL:  url(),
        PORT:          port().default(3000),
        LOG_LEVEL:     str().enum(['debug', 'info', 'warn', 'error']).default('info'),
        ENABLE_CACHE:  bool().default(true),
        REDIS_URL:     url().optional(),
      },
      client: {},
      runtimeEnv: {
        DATABASE_URL:  process.env.DATABASE_URL,
        PORT:          process.env.PORT,
        LOG_LEVEL:     process.env.LOG_LEVEL,
        ENABLE_CACHE:  process.env.ENABLE_CACHE,
        REDIS_URL:     process.env.REDIS_URL,
      },
      adapter: 'node',
    })
    ```

    <Note>
      `next-safe-env` validates what is already in `process.env`. It does not load `.env` files. Use `dotenv` or your platform's native secret injection to populate `process.env` before `createEnv` runs.
    </Note>
  </Step>

  <Step title="Use it in your server">
    Import `env` anywhere in your application. Numeric vars like `PORT` are already coerced to `number` - no `parseInt` needed.

    ```typescript theme={null}
    // server.ts
    import { env } from './config/env.js'

    app.listen(env.PORT, () => {
      console.log(`Listening on port ${env.PORT}`)  // number, not string
    })
    ```

    Every other variable is available with its correct inferred type:

    ```typescript theme={null}
    import { env } from './config/env.js'

    env.DATABASE_URL  // string
    env.PORT          // number
    env.LOG_LEVEL     // 'debug' | 'info' | 'warn' | 'error'
    env.ENABLE_CACHE  // boolean
    env.REDIS_URL     // string | undefined
    ```
  </Step>
</Steps>

## The node adapter

The `node` adapter is intentionally minimal. Both of its hooks - `beforeValidate` and `afterValidate` - are no-ops. Raw env values go in, validated and coerced values come out unchanged. There are no prefix rules, no key-stripping, and no browser-context checks. Every var from both `server` and `client` schemas is present in the returned object.

This makes the `node` adapter appropriate for any environment where you control the full process and want all variables available without restriction.

## Auto-detection

When you omit `adapter`, `next-safe-env` selects the adapter based on the runtime environment:

| Condition                                  | Adapter selected |
| ------------------------------------------ | ---------------- |
| `process` is undefined or has no `version` | `edge`           |
| `process.env.NEXT_RUNTIME` is set          | `nextjs`         |
| Otherwise                                  | `node`           |

In a plain Node.js process where `NEXT_RUNTIME` is not set, `node` is always selected. Setting `adapter: 'node'` explicitly is recommended for clarity and to make your intent self-documenting.

## What happens when validation fails

If any variable is missing or invalid, the process exits before serving a single request and prints every problem at once:

```
[next-safe-env] Environment validation failed - 2 error(s):

  ✗ DATABASE_URL     - Required. Expected a valid URL. Got: undefined
  ✗ PORT             - Invalid port. Must be 1–65535. Got: "99999"

  Server vars:  3 valid, 2 invalid
  Client vars:  0 valid, 0 invalid

  Set the correct values in your .env file or deployment environment and restart.
```
