https://github.com/mzeeshanwahid/next-safe-env
Typed, validated environment variables for Next.js and Node.js
https://github.com/mzeeshanwahid/next-safe-env
configuration dotenv edge-runtime env-validation environment-variables next-publix nextjs nodejs server-only type-safe typescript vite zero-dependency
Last synced: 7 days ago
JSON representation
Typed, validated environment variables for Next.js and Node.js
- Host: GitHub
- URL: https://github.com/mzeeshanwahid/next-safe-env
- Owner: mzeeshanwahid
- License: mit
- Created: 2026-04-28T12:20:40.000Z (about 2 months ago)
- Default Branch: main
- Last Pushed: 2026-05-06T16:09:31.000Z (about 1 month ago)
- Last Synced: 2026-05-06T18:17:10.301Z (about 1 month ago)
- Topics: configuration, dotenv, edge-runtime, env-validation, environment-variables, next-publix, nextjs, nodejs, server-only, type-safe, typescript, vite, zero-dependency
- Language: TypeScript
- Homepage: https://next-safe-env.dev
- Size: 894 KB
- Stars: 3
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Contributing: CONTRIBUTING.md
- License: LICENSE
- Agents: AGENTS.md
Awesome Lists containing this project
README
next-safe-env
Typed, validated environment variables for Next.js and Node.js. Crash at startup, never at runtime.
---
## The Problem
Every Next.js and Node.js project has the same boilerplate:
```ts
const DATABASE_URL = process.env.DATABASE_URL
if (!DATABASE_URL) throw new Error('Missing DATABASE_URL')
const PORT = parseInt(process.env.PORT ?? '3000', 10)
if (isNaN(PORT)) throw new Error('PORT must be a number')
```
`process.env.X` is always `string | undefined` - no types, no autocomplete. Missing or malformed vars surface mid-request, not at startup. Nothing stops you from reading a server secret in a client component and getting a silent `undefined` in the browser. Every project re-writes the same validation logic with no single place to audit what the app needs to run.
`next-safe-env` fixes all of this with a single function call.
---
## Why `next-safe-env`?
| Feature | **next-safe-env** | t3-env | envalid | dotenv + Zod |
|---|:---:|:---:|:---:|:---:|
| Zero dependencies | ✅ | ❌ | ❌ | ❌ |
| Next.js App Router support | ✅ | Partial | ❌ | ❌ |
| Server/client TypeScript split | ✅ | ✅ | ❌ | Manual |
| Edge Runtime adapter | ✅ | ❌ | ❌ | ❌ |
| Fluent validator API | ✅ | Schema-based | Custom | Schema-based |
| Pretty error output | ✅ | Partial | ✅ | Manual |
| Bundle size | **< 5 kB** | ~50 kB+ | ~10 kB | ~50 kB+ |
| Auto-enforce `NEXT_PUBLIC_` prefix | ✅ | Manual | ❌ | ❌ |
| Zod interop | Optional ✅ | Required | ❌ | Required |
| `ClientEnv` server-only branding | ✅ | Partial | ❌ | Manual |
| Vite adapter | ✅ | ✅ | ❌ | Manual |
| CLI (`check` / `init`) | ✅ | ❌ | ❌ | ❌ |
If your project already uses a schema validation library, tools like `t3-env` or `envalid` integrate well with your existing setup. `next-safe-env` is for teams that want typed, validated env vars with no additional dependencies - the full feature set ships in under 5 kB.
---
## Requirements
- **Node.js** 18+
- **TypeScript** 5.x
No runtime dependencies.
---
## Installation
```bash
npm install next-safe-env
# or
pnpm add next-safe-env
# or
yarn add next-safe-env
```
> `next-safe-env` validates what is already in `process.env`. It does not load `.env` files. For that, use Next.js's built-in `.env` support or `dotenv`.
---
## Quick Start
```ts
// src/env.ts
import { createEnv, str, url, port, bool } from 'next-safe-env'
export const env = createEnv({
server: {
DATABASE_URL: url(), // must be a valid URL
PORT: port().default(3000), // coerced to number, defaults to 3000
NODE_ENV: str().enum(['development', 'production', 'test']),
},
client: {
NEXT_PUBLIC_APP_NAME: str().default('My App'),
NEXT_PUBLIC_ENABLE_DEBUG: bool().default(false),
},
runtimeEnv: {
DATABASE_URL: process.env.DATABASE_URL,
PORT: process.env.PORT,
NODE_ENV: process.env.NODE_ENV,
NEXT_PUBLIC_APP_NAME: process.env.NEXT_PUBLIC_APP_NAME,
NEXT_PUBLIC_ENABLE_DEBUG: process.env.NEXT_PUBLIC_ENABLE_DEBUG,
},
})
```
```ts
// anywhere in your app
import { env } from '@/env'
env.DATABASE_URL // string
env.PORT // number - not string
env.NEXT_PUBLIC_APP_NAME // string
```
If any variable is missing or invalid, the app refuses to start and prints every problem at once:
```
[next-safe-env] Environment validation failed - 3 error(s):
✗ DATABASE_URL - Required. Expected a valid URL. Got: "postgres-localhost"
✗ JWT_SECRET - Too short. Must be ≥ 32 characters. Got length: 12
✗ SMTP_PORT - Invalid port. Must be 1–65535. Got: "99999"
```
---
## CLI
`next-safe-env` ships a zero-install CLI for validation and scaffolding.
### `check` — validate before you deploy
Imports your compiled env file in an isolated process and exits `0` if all vars are valid, `1` if any fail. Drop it into any CI pipeline to gate deployments:
```bash
# Auto-discovers src/env.js then dist/env.js
npx next-safe-env check
# Or point at a specific file
npx next-safe-env check ./dist/env.js
```
```
[next-safe-env] Checking src/env.js...
[next-safe-env] Environment validation failed — 2 error(s):
✗ DATABASE_URL — Expected valid URL. Got: "postgres-localhost"
✗ JWT_SECRET — Expected length >= 32. Got length: 12
[next-safe-env] ✗ Validation failed.
```
### `init` — generate `src/env.ts` and `.env.example`
An interactive scaffold that asks which variables your app needs, their types, defaults, and constraints — then writes a ready-to-use `env.ts` and a commented `.env.example`:
```bash
npx next-safe-env init
# Custom output path
npx next-safe-env init --output config/env.ts
```
The generated `.env.example` includes inline comments for every variable so new contributors know exactly what to fill in:
```dotenv
# DATABASE_URL — required valid URL
DATABASE_URL=
# PORT — required port number (1–65535)
# Default: 3000
PORT=3000
# NODE_ENV — required string
# Allowed values: development | production | test
NODE_ENV=
```
---
## Documentation
The full documentation is available at **[next-safe-env.dev](https://next-safe-env.dev)**.
### Guides
- [Getting Started](https://next-safe-env.dev/quickstart) - Install and validate your first env var in minutes
- [Next.js App Router](https://next-safe-env.dev/guides/nextjs) - Server/client splitting with automatic `NEXT_PUBLIC_` enforcement
- [Node.js](https://next-safe-env.dev/guides/nodejs) - Plain Node.js servers, APIs, and CLI scripts
- [Edge Runtime](https://next-safe-env.dev/guides/edge-runtime) - Vercel Edge Runtime and Next.js Middleware
- [Vite](https://next-safe-env.dev/guides/vite) - Non-Next.js React apps with `import.meta.env`
- [Zod Interop](https://next-safe-env.dev/guides/zod-interop) - Pass `z.object(...)` schemas directly, no rewrites needed
- [Testing](https://next-safe-env.dev/guides/testing) - Skip validation in test environments without removing your schema
- [CLI](https://next-safe-env.dev/guides/cli) - `check` and `init` commands for CI validation and interactive scaffolding
### API Reference
- [Validators](https://next-safe-env.dev/api/validators) - `str`, `num`, `bool`, `url`, `port` and their chainable rules
- [createEnv()](https://next-safe-env.dev/api/create-env) - Full reference for every configuration option
- [TypeScript Types](https://next-safe-env.dev/api/types) - `ServerOnly`, `ClientEnv`, and all exported types
### Concepts
- [Adapters](https://next-safe-env.dev/concepts/adapters) - How Next.js, Node.js, Edge Runtime, and Vite adapters work
- [Server & Client Split](https://next-safe-env.dev/concepts/server-client-split) - How env vars are separated and protected per runtime context
- [Error Handling](https://next-safe-env.dev/concepts/error-handling) - Validation errors, pretty output, and custom error handlers
---
`next-safe-env` was built because `process.env.X` should never be `string | undefined` in a typed codebase - and getting full validation, type inference, and Next.js adapter support shouldn't require adding new dependencies to do it.
MIT © 2026