yieldless/env
Tuple helpers for required environment variables and schema-backed config parsing.
yieldless/env keeps configuration loading explicit without inventing a config framework. It helps with the parts that are easy to get wrong: missing variables, empty variables, selecting only the keys a service cares about, and handing the result to the same schema adapters used elsewhere.
Exports
readEnv(source, key, options): SafeResult<string, EnvVarError>readOptionalEnv(source, key, options): SafeResult<string | undefined, EnvVarError>pickEnv(source, keys): PickedEnvparseEnvSafe(schema, source = process.env): SafeResult<T, E>parseEnvAsyncSafe(schema, source = process.env): Promise<SafeResult<T, E>>class EnvVarError extends Error
Required values
import { readEnv } from "yieldless/env";
const [error, databaseUrl] = readEnv(process.env, "DATABASE_URL");
if (error) {
return [error, null] as const;
}Empty strings are treated as errors by default. Pass allowEmpty: true when an empty string is meaningful.
Schema-backed config
import { parseEnvSafe, pickEnv } from "yieldless/env";
const [error, env] = parseEnvSafe(
envSchema,
pickEnv(process.env, ["DATABASE_URL", "PORT", "NODE_ENV"] as const),
);The schema can be anything supported by yieldless/schema: safeParse(), parse(), safeParseAsync(), or parseAsync().
Behavior notes
parseEnvSafe()defaults toprocess.envwhen it exists.pickEnv()is useful for tests and for avoiding accidental coupling to unrelated variables.EnvVarError.codeis eitherERR_ENV_MISSINGorERR_ENV_EMPTY.readOptionalEnv()returns[null, undefined]for missing values but still rejects empty strings unlessallowEmptyis enabled.
Good
Validate configuration once at startup or at the outer service boundary.
const [error, config] = parseEnvSafe(
configSchema,
pickEnv(process.env, ["DATABASE_URL", "PORT", "NODE_ENV"] as const),
);
if (error) {
console.error(error);
process.exitCode = 1;
}Use readEnv() for one-off scripts where a full schema would add noise.
const [tokenError, token] = readEnv(process.env, "GITHUB_TOKEN");
if (tokenError) return [tokenError, null] as const;Avoid
Do not read process.env deep inside business logic.
export async function sendEmail(user: User) {
const apiKey = process.env.MAILER_API_KEY;
return mailer.send(apiKey, user.email);
}Parse once, then pass config explicitly or inject it at the edge.