npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

next-safe-env

v1.0.0

Published

Typed, validated environment variables for Next.js and Node.js

Readme


The Problem

Every Next.js and Node.js project has the same boilerplate:

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<T> 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

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

// 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,
  },
})
// 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:

# 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:

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:

# 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.

Guides

  • Getting Started - Install and validate your first env var in minutes
  • Next.js App Router - Server/client splitting with automatic NEXT_PUBLIC_ enforcement
  • Node.js - Plain Node.js servers, APIs, and CLI scripts
  • Edge Runtime - Vercel Edge Runtime and Next.js Middleware
  • Vite - Non-Next.js React apps with import.meta.env
  • Zod Interop - Pass z.object(...) schemas directly, no rewrites needed
  • Testing - Skip validation in test environments without removing your schema
  • CLI - check and init commands for CI validation and interactive scaffolding

API Reference

  • Validators - str, num, bool, url, port and their chainable rules
  • createEnv() - Full reference for every configuration option
  • TypeScript Types - ServerOnly<T>, ClientEnv<T>, and all exported types

Concepts

  • Adapters - How Next.js, Node.js, Edge Runtime, and Vite adapters work
  • Server & Client Split - How env vars are separated and protected per runtime context
  • 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