Bun fullstack dev server
Learn how to use ArkEnv in a Bun fullstack application.
A plain Bun script can call arkenv() with no extra plugin. Apps that
bundle a client with
Bun's fullstack dev server
(Bun.serve()) or Bun's bundler
(Bun.build()) need @arkenv/bun-plugin so the client bundle cannot
see server secrets.
Backend-only script?
Stop at Getting started. You do not need this plugin when nothing ships to a browser.
Quickstart
arkenv init is the default when the app uses Bun.serve() or
Bun.build(). It installs @arkenv/bun-plugin and writes env.ts.
npx arkenv initManual installation
Already ran init?
Skip this section. The CLI already installed the plugin and wrote the schema.
Install @arkenv/bun-plugin next to the engine you write schemas with,
when bunfig.toml or the Bun.build script is already yours.
ArkType engine
Install @arkenv/core, arktype, and the Bun plugin:
npm install @arkenv/core arktype
npm install -D @arkenv/bun-pluginStandard Schema engine
If you aren't using ArkType, install @arkenv/standard with Zod or
Valibot, then import the plugin from @arkenv/bun-plugin/standard:
npm install @arkenv/standard zod
npm install -D @arkenv/bun-pluginnpm install @arkenv/standard valibot @valibot/to-json-schema
npm install -D @arkenv/bun-pluginFull-stack setup
A full-stack Bun app has two graphs: the server process, and the client
bundle. The plugin has to be on both the dev server and Bun.build, or
the client graph still imports the real schema.
Define your schema
Create your schema in src/env.ts:
import from "@arkenv/core";
export const = ({
DATABASE_URL: "string",
BUN_PUBLIC_API_URL: "string",
BUN_PUBLIC_DEBUG: "boolean = false",
});Configure dev server
Enable the plugin for development in bunfig.toml:
[serve.static]
plugins = ["@arkenv/bun-plugin"]Configure production build
Pass the plugin to Bun.build in your build script:
import from "@arkenv/bun-plugin";
await Bun.build({
: ["./src/index.tsx"],
outdir: "./dist",
: [()],
});Import { env } from "./env" in application code. Transform mode inlines public
BUN_PUBLIC_ keys into the client bundle and protects server secrets.
Backend-only Bun
No client bundle?
Skip @arkenv/bun-plugin. Call the runtime directly. The plugin is
for Bun.serve() and Bun.build() client graphs.
import from "@arkenv/core";
export const = ({
DATABASE_URL: "string",
PORT: "number.port = 3000",
});Client access
Import the validated env object from your schema module.
Do not read process.env directly
Reading process.env skips ArkEnv. Import { env } so public keys stay
typed and coerced and server secrets stay out of the client bundle.
Types and options: @arkenv/bun-plugin.
SSR and bundling execution
Full-stack Bun applications separate server runtime execution from client bundle generation:
- Server execution: Server entrypoints and backend APIs execute real
@arkenv/coreor@arkenv/standardvalidation at startup, validating database URLs and backend secrets before serving traffic. - Client bundling: During
Bun.buildruns or[serve.static]requests,@arkenv/bun-plugintransformsenv.tsimports in client code. It inlinesBUN_PUBLIC_*values as static literals and replaces server keys with throwing runtime stubs. Browser bundles omit the validator engine.