Quickstart
Define and materialize composable server and client environment Effects.
bun add @ayronforge/envil effect
Define the environment
import {
client,
createEnv,
port,
redacted,
requiredString,
server,
shared,
url,
} from "@ayronforge/envil";
export const appEnv = createEnv(
shared({
APP_NAME: "My App",
}),
server({
DATABASE_URL: redacted(requiredString),
PORT: port,
}),
client(
{
APP_URL: url,
},
{
runtimeEnv: import.meta.env,
prefix: "VITE_",
},
),
);
Server fragments default to process.env. A client fragment containing schemas
requires an explicit runtimeEnv, because browser frameworks expose public
configuration differently. Static values may also be declared directly in
server(...) or client(...).
Run the desired Effect
import { Effect } from "effect";
const serverEnv = Effect.runSync(appEnv.server);
const clientEnv = await Effect.runPromise(appEnv.client);
Both properties are lazy Effects. The caller chooses synchronous or asynchronous execution. If a schema or resolver is asynchronous, run the Effect asynchronously.
The client output contains client and shared values. The server output contains all three targets, matching what a server bundle can use.
Protect client builds
For Vite applications, add the Envil plugin:
import { defineConfig } from "vite";
import envil from "@ayronforge/envil/plugins/vite";
export default defineConfig({
plugins: [envil()],
});
The plugin removes every server(...) fragment expression before the client
module graph is bundled and injects an exact runtime target. It does not need to
inspect or evaluate your schemas, resolver adapters, spreads, or factory calls.
See Build and Runtime Security for Rollup, Rolldown, webpack, Rspack, and esbuild setup.