Node / TypeScript SDK
Read variables that change while your process runs, and rebuild the things made from them, without a restart.
Install
npm i @dynv/sdkHow it works
The SDK never talks to Dynv. It talks to the small agent that dynv run starts next to your app, over loopback, using two variables dynv run puts in your environment: DYNV_AGENT_URL and DYNV_AGENT_KEY. The agent has already verified and decrypted the data, so the SDK holds no token and does no cryptography.
Not running under dynv run (local development, tests, a migration in progress)? Then get() just reads process.env and no change events ever fire, so the same code works everywhere.
Read variables
Mark the variables you want live as hot (dynv admin mode web hot PROXY_SERVER or in the dashboard), start your app with dynv run, then:
import { dynv } from "@dynv/sdk";
const d = dynv(); // one shared instance per process
await d.ready(); // resolves after the first snapshot (immediately without an agent)
d.get("PROXY_SERVER"); // string | undefined, synchronous, always current
d.require("API_KEY"); // same, but throws "dynv: API_KEY is not set"
d.pick(["PROXY_SERVER", "PROXY_USERNAME"]); // several keys from one consistent snapshot
d.version; // the document version you are looking at
d.live; // true when running under dynv runget()returns the current value. For a key Dynv has never managed it falls back toprocess.env; once Dynv has managed a key, removing it makes it read asundefined, never as a stale inherited value.- Read on every use. Do not cache
d.get(...)in a module-level constant, or you will pin the old value.
React to changes
import { dynv } from "@dynv/sdk";
const d = dynv();
const off = d.on("change", ({ version, changed }) => {
// changed = names of variables added, changed or removed. Never values.
console.log("env changed", version, changed);
});
d.on("error", (err) => console.warn("dynv agent:", err));
// later: off(); d.close();The first snapshot is the starting point, not a change, so you do not get an event at startup. If the agent is briefly unreachable the SDK keeps retrying with backoff (500 ms up to 30 s) and emits error each time.
By default, hot changes are also written into process.env so libraries that read it lazily see them. Turn that off with new Dynv({ mirrorToProcessEnv: false }).
Rebuild long-lived objects: resource()
A proxy agent, an API client or a connection pool is built from variables once and used for a long time. resource() rebuilds it when its inputs change, and lets in-flight work finish on the old one.
import { dynv } from "@dynv/sdk";
import { ProxyAgent, request } from "undici";
const proxy = dynv().resource(
["PROXY_SERVER", "PROXY_USERNAME", "PROXY_PASSWORD"],
(env) =>
new ProxyAgent({
uri: env.PROXY_SERVER!,
token: "Basic " + Buffer.from(`${env.PROXY_USERNAME}:${env.PROXY_PASSWORD}`).toString("base64"),
}),
{ dispose: (old) => old.destroy(), drainTimeoutMs: 30_000 },
);
export function get(url: string) {
// The lease keeps this agent alive until the request is done,
// even if the proxy is rotated in the meantime.
return proxy.use((agent) => request(url, { dispatcher: agent }));
}- The new instance is built first. If your factory throws, the old one stays current and an
errorevent fires. - The swap is atomic: every
use()oracquire()after it gets the new instance. - The old instance is disposed after its in-flight work drains (at most
drainTimeoutMs, default 30 s) and afteroverlapMs(default 0). - During an
overlapMswindow,previous()returns the old instance, so you can retry with the old credential while a provider propagates a new one. generationandisStale(gen)let pools recycle a stale item at its next lease instead of mid-job.
| Member | What it does |
|---|---|
use(fn) | Runs fn(value, generation) holding a lease; releases it when done. |
acquire() | Returns { value, generation, release }. Call release() yourself. |
current() | The current instance, without a lease. |
previous() | The retired instance during an overlap window, else undefined. |
generation, isStale(gen) | Bumps on every swap; true if something built at gen is out of date. |
on("swap" | "error", fn) | A swap event carries { generation, changed }. |
close() | Stops listening and disposes the current instance. |
Options
new Dynv({
agentUrl, // default process.env.DYNV_AGENT_URL
agentKey, // default process.env.DYNV_AGENT_KEY
mirrorToProcessEnv, // default true
onError, // called on agent errors, in addition to the "error" event
});The agent's own HTTP endpoints, if you are on another language, are on the HTTP API page. Other SDKs are Coming soon.