Loading Secrets

How to initialize Redenv and load secrets into your application.

The Redenv client provides two methods for loading secrets: init() and load().

MethodReturnsUse Where
init()Promise<Secrets>Only in lib/redenv.ts (app startup)
load()Promise<Secrets>Everywhere else (programmatic access)

Both methods fetch, decrypt, cache secrets, and populate process.env.

Info

Fun fact: init() and load() are identical under the hood — both return Secrets. The different names make your code's intent clearer: "initialize at startup" vs "load secrets for use".

Step 1: Initialize and Validate at Startup#

Call init() only in the file where you create the client. Since init() returns the Secrets object, you can validate required secrets immediately:

lib/redenv.ts
import { Redenv } from "@redenv/client";

export const redenv = new Redenv({
  project: process.env.REDENV_PROJECT!,
  tokenId: process.env.REDENV_TOKEN_ID!,
  token: process.env.REDENV_TOKEN_KEY!,
  upstash: {
    url: process.env.UPSTASH_REDIS_URL!,
    token: process.env.UPSTASH_REDIS_TOKEN!,
  },
  environment: process.env.NODE_ENV || "development",
});

// Initialize and validate at startup
const secrets = await redenv.init(); 
secrets.require("DATABASE_URL", "JWT_SECRET", "STRIPE_KEY"); 

Tip

Fail fast! By validating in lib/redenv.ts, your app crashes immediately on startup if secrets are missing — not later when a route tries to use them.

Step 2: Use load() Everywhere Else#

In all other files, use load() to get programmatic access to secrets:

services/database.ts
import { redenv } from "@/lib/redenv";

export async function connectDatabase() {
  const secrets = await redenv.load(); 

  // Programmatic access (recommended)
  const dbUrl = secrets.DATABASE_URL;
  const port = secrets.get("PORT", 5432).toInt();

  // Validate required secrets
  secrets.require("DATABASE_URL", "DATABASE_PASSWORD");
}
services/stripe.ts
import { redenv } from "@/lib/redenv";

export async function createPayment(amount: number) {
  const secrets = await redenv.load(); 

  const stripe = new Stripe(secrets.STRIPE_SECRET_KEY);
  // ...
}

Info

load() is idempotent. Calling it multiple times won't cause extra network requests—it serves from cache within the configured TTL.

Why Programmatic Access?#

We highly recommend using load() over direct process.env access:

Featureprocess.envsecrets object
Type casting Manual parsing .toInt(), .toBool(), .toJSON()
Validation Runtime errors .require() fails fast
Scoping Manual filtering .scope("AWS_")
Masking Exposed in logs Auto-masked
Raw access .raw for unexpanded values
const secrets = await redenv.load();

// Type-safe casting
const port = secrets.get("PORT", 3000).toInt();
const debug = secrets.get("DEBUG").toBool();
const config = secrets.get("APP_CONFIG").toJSON();

// Fail fast if required secrets are missing
secrets.require("DATABASE_URL", "STRIPE_KEY", "JWT_SECRET");

// Scoped access for modules
const awsConfig = secrets.scope("AWS_"); // AWS_KEY → KEY

Serverless Functions#

For serverless environments (Vercel, Lambda), you can skip init() and use load() directly:

api/handler.ts
import { redenv } from "@/lib/redenv";

export async function handler(req, res) {
  // First call fetches from Redis, subsequent calls use cache
  const secrets = await redenv.load();

  secrets.require("API_KEY");
  res.json({ status: "ok" });
}

Info

In serverless, load() handles both initialization and access. The first invocation warms the cache; subsequent calls in the same instance are instant.

Environment Population#

By default, both init() and load() populate process.env:

const secrets = await redenv.load();

// These are equivalent:
secrets.API_KEY === process.env.API_KEY; // true

Controlling Override Behavior#

lib/redenv.ts
export const redenv = new Redenv({
  // ...
  env: {
    override: false, // Preserve existing env vars (useful for local .env overrides)
  },
});

Multiple Environments#

lib/redenv.ts
import { Redenv } from "@redenv/client";

const baseOptions = {
  project: process.env.REDENV_PROJECT!,
  tokenId: process.env.REDENV_TOKEN_ID!,
  token: process.env.REDENV_TOKEN_KEY!,
  upstash: {
    url: process.env.UPSTASH_REDIS_URL!,
    token: process.env.UPSTASH_REDIS_TOKEN!,
  },
};

export const prodSecrets = new Redenv({
  ...baseOptions,
  environment: "production",
});

export const stagingSecrets = new Redenv({
  ...baseOptions,
  environment: "staging",
});

Warning

Multiple environments will both try to populate process.env. Use env: { override: false } or rely solely on programmatic access.