Loading Secrets
How to initialize Redenv and load secrets into your application.
The Redenv client provides two methods for loading secrets: init() and load().
| Method | Returns | Use 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".
Recommended Setup#
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:
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:
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");
}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:
| Feature | process.env | secrets 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 → KEYServerless Functions#
For serverless environments (Vercel, Lambda), you can skip init() and use load() directly:
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; // trueControlling Override Behavior#
export const redenv = new Redenv({
// ...
env: {
override: false, // Preserve existing env vars (useful for local .env overrides)
},
});Multiple Environments#
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.