README.md

July 31, 2026 · View on GitHub

envapt logo

envapt

The apt way to read typed config.
Read config from any source as real typed values, with zero runtime dependencies.

npm downloads jsr CI License


envapt returns config as the type you asked for instead of the string | undefined you get raw, with a fallback that removes undefined from the return type. It reads from whatever source you bind. On Node, Bun, and Deno that is process.env and your .env files, bound on import. On Cloudflare Workers, in the browser, or for a secrets object you fetched at boot, you bind the source with Envapter.useSource(...).

import { Converters, Envapter } from 'envapt';

const { port, databaseUrl, cacheTtl, allowedOrigins } = Envapter.getRequiredAll(
    {
        PORT: Converters.Port, // number, checked against 0-65535
        DATABASE_URL: Converters.Url, // URL
        CACHE_TTL: Converters.Time, // "15m" parsed to 900000
        ALLOWED_ORIGINS: Converters.array() // string[]
        // ... and many more built-in converters
    },
    'camelCase'
);

Call that at startup and a missing value fails the boot, with one error naming every key that was missing. Reads with a fallback (Envapter.getNumber('WORKERS', 4)) never throw.

Read the docs →

What you get

  • Typed values. A fallback removes undefined from the return type. Built-in converters cover numbers, integers, floats, booleans, bigint, symbols, JSON, URLs, regular expressions, dates, durations, ports, emails, and arrays, or pass your own function or a Standard Schema validator (zod, valibot, arktype).
  • Any source. A source is any object with a readVars() method, so you can bind process.env, a Cloudflare Workers binding, a browser bundle, or a secrets payload you fetched from a store at boot. On Node, Bun, and Deno one binds on import.
  • Zero runtime dependencies. The reader, converters, and built-in .env parser are self-contained, so nothing is added to your dependency tree.
  • Runs on Node, Bun, Deno, Cloudflare Workers, and the browser. Node >=20, Bun >=1.3, Deno >=2.5 (ESM and CJS). The portable build resolves through the package exports conditions.
  • .env loading built in on Node. The default Node source adds a per-environment file cascade, ${VAR} templates, and strict / required checks. Off Node there is no filesystem, so you bind another source with Envapter.useSource(...) and read with the same typed API.

Install

npm install envapt
pnpm add envapt
yarn add envapt
bun add envapt
deno add jsr:@materwelon/envapt

Quick start

Read values functionally with Envapter, or bind them to class fields with the @Envapt decorator. Both share the same parsing, converters, and cache.

Functional

Read a value from any call site, in JavaScript or TypeScript. No build step. On Node the source is bound for you. On Workers and in the browser, call Envapter.useSource(...) first.

import { Converters, Envapter } from 'envapt';

const requestTimeout = Envapter.getUsing('REQUEST_TIMEOUT', Converters.Time, '30s'); // 30000
const maxRetries = Envapter.getNumber('MAX_RETRIES', 3);
const debug = Envapter.getBoolean('DEBUG', false);

On Cloudflare Workers, env is importable at module scope, so bind it once in a config module, and in the browser seed a PortableSource from the object your bundler injects.

import { env } from 'cloudflare:workers';
import { Envapter, PortableSource } from 'envapt';

Envapter.useSource(new PortableSource(env));

export const apiToken = Envapter.get('API_TOKEN');

Decorator

Bind a value to a class field with a TC39 accessor decorator. No experimentalDecorators flag, and it runs on Bun and Deno from .ts directly.

import { Converters, Envapt, EnvTime } from 'envapt';

class Config {
    @Envapt('PORT', { converter: Converters.Port, fallback: 3000 })
    static accessor port: number;

    @EnvTime('CACHE_TTL', '15m')
    static accessor cacheTtl: number;
}

The legacy (experimentalDecorators) decorators are exported from envapt/legacy.

Agent skill

Install the envapt agent skill so AI coding tools use the correct API:

npx skills add materwelonDhruv/envapt

Built by @materwelonDhruv · Apache 2.0