There are a few different ways to integrate Varlock into a JavaScript / Node.js application.
Some tools/frameworks may require an additional package, or have more specific instructions. Check the Integrations section in the navigation for more details.
Want to help us build more integrations? Join our Discord!
Node.js - varlock/auto-load
Section titled “Node.js - varlock/auto-load”The best way to integrate varlock into a plain Node.js application (⚠️ version 22 or higher) is to import the varlock/auto-load module. This uses execSync to call out to the varlock CLI (or reuses env injected by a parent varlock run), sets resolved env vars into process.env, and initializes varlock’s runtime code, including:
- varlock’s
ENVobject - log redaction (if enabled)
- leak detection (if enabled)
import 'varlock/auto-load';import { ENV } from 'varlock/env';
const FROM_VARLOCK_ENV = ENV.MY_CONFIG_ITEM; // ✨ recommendedconst FROM_PROCESS_ENV = process.env.MY_CONFIG_ITEM; // 🆗 still worksReporting load failures
Section titled “Reporting load failures”When validation fails, varlock/auto-load writes the error to stderr and exits with a non-zero code. Because it exits during module import, an error reporter like Sentry may never see the failure. You can opt in to having auto-load throw the error instead of exiting, so a reporter can pick it up. This is never enabled automatically, so nothing changes for apps that do not opt in.
If your reporter is already initialized before varlock loads (for example Sentry started via node --import ./instrument.mjs, with its DSN in a real environment variable), set _VARLOCK_THROW_ON_LOAD_ERROR=1. auto-load then throws instead of exiting, and the reporter’s existing uncaughtException handler catches it. Make sure the reporter is imported before varlock:
import './instrument.js'; // initializes Sentry (registers its handler)import 'varlock/auto-load';If the DSN itself comes from varlock, or you init your reporter in app code, set a globalThis._varlockOnLoadError hook. It is called with the error and a map of the values that did resolve (so you can read SENTRY_DSN even though the overall load failed):
import * as Sentry from '@sentry/node';
globalThis._varlockOnLoadError = (err, env) => { Sentry.init({ dsn: env.SENTRY_DSN }); Sentry.captureException(err); return Sentry.close(2000); // return the promise; auto-load exits once it settles};import './varlock-error-hook.js'; // must be imported BEFORE auto-loadimport 'varlock/auto-load';Two things to keep in mind:
- The hook must be registered before
varlock/auto-loadis imported. ES modules hoist allimportstatements, so register the hook via its own side-effect import placed above the auto-load import, not with an inline call between imports. - Reporting is best-effort. auto-load does not
awaityour hook (that would change how env is injected), so it gives async work a short window and then forces the process to exit. Return a promise from the hook and auto-load will exit as soon as it settles.
Only resolved values are available to the hook, so this does not help with .env parse or schema errors, where resolution is skipped entirely. To report those, the DSN must come from a real environment variable and be picked up by an already-initialized reporter.
Reusing an injected env blob
Section titled “Reusing an injected env blob”When the process was launched by varlock run, the resolved env is already present as the __VARLOCK_ENV blob. auto-load reuses it instead of calling the CLI again, so wrapping an auto-load app in varlock run does not cost a second resolution.
Reuse only happens when a fresh resolution would produce the same result:
- the blob was resolved in the same directory the app would resolve in (a root-level
varlock runin a monorepo does not stop per-package resolution) - it resolved without errors
- no env override recorded in the blob has changed since (
varlock run -- sh -c 'FOO=x node app.js're-resolves, so the newFOOwins)
If any check fails, auto-load falls back to the CLI. Control it explicitly with _VARLOCK_USE_INJECTED_ENV: 0 always re-resolves, 1 always trusts the blob, skipping the directory check. varlock run applies the same rules when it finds a blob, which covers non-Node workloads. Trust mode is how you hand an env into an environment with no .env files at all, like a remote sandbox; see the E2B and Fly.io guides.
Boot via varlock run
Section titled “Boot via varlock run”A less invasive way to use varlock with your application is to run your application via varlock run.
varlock run -- <your-command>This will load and validate your environment variables, then run the command you provided with those environment variables injected into the process. This will not inject any runtime code, and varlock’s ENV object will not be available.
If you have installed varlock as a project dependency instead of globally, you should run this via your package manager:
npm exec -- varlock run -- <your-command>pnpm exec -- varlock run -- <your-command>bunx varlock run -- <your-command>vlx -- varlock run -- <your-command>yarn exec -- varlock run -- <your-command>In package.json scripts, calling varlock directly will work, as your package manager handles path issues:
"scripts": { "start": "varlock run -- node index.js"}Even when using a deeper integration for your code, you may still need to use varlock run when calling external scripts/tools, like database migrations, to pass along resolved env vars.
Front-end frameworks
Section titled “Front-end frameworks”While environment variables are not available in the browser, many frameworks expose some env vars that are available at build time to the client by embedding them into your bundled code. This is best accomplished using tool-specific integrations, especially for frameworks that are handling both client and server-side code.