nanvc is a small TypeScript client for the HashiCorp Vault HTTP API.
Full documentation is available at zailic.github.io/nanvc. Release notes are available in the changelog.
New development is focused on VaultClientV2, the typed client built on top of RawVaultClient.
The original VaultClient remains available for compatibility, but it is expected to be deprecated and removed in a future major release.
- Node.js
>= 20 - npm
npm install nanvcimport { VaultClientV2 } from 'nanvc';
const vault = new VaultClientV2({
clusterAddress: 'http://127.0.0.1:8200',
authToken: process.env.VAULT_TOKEN ?? null,
});
async function main() {
await vault.write('secret/my-app/my-secret', {
foo: 'my-password',
}).unwrap();
const secret = await vault.read('secret/my-app/my-secret').unwrap();
console.log(secret.foo);
}
main().catch(console.error);VaultClient is the original API shape based on command specs and VaultResponse.
The VaultClient constructor accepts three optional arguments:
- Vault cluster address
- Vault auth token
- Vault API version
If omitted, the client uses:
NANVC_VAULT_CLUSTER_ADDRESSNANVC_VAULT_AUTH_TOKENNANVC_VAULT_API_VERSION
and falls back to:
- cluster address:
http://127.0.0.1:8200 - auth token:
null - API version:
v1
import VaultClient from 'nanvc';
const vault = new VaultClient('http://127.0.0.1:8200', process.env.VAULT_TOKEN ?? null);
async function main(): Promise<void> {
const writeResponse = await vault.write('/secret/my-app/my-secret', {
foo: 'my-password',
});
if (!writeResponse.succeeded) {
throw new Error(writeResponse.errorMessage ?? 'Vault write failed');
}
const readResponse = await vault.read('/secret/my-app/my-secret');
if (!readResponse.succeeded || !readResponse.apiResponse) {
throw new Error(readResponse.errorMessage ?? 'Vault read failed');
}
const secret = readResponse.apiResponse as { data?: { foo?: string } };
console.log(secret.data?.foo);
}
main().catch(console.error);Starting with version 1.2.0, nanvc is published as an ESM-only package. CommonJS consumers need to use dynamic import() to load the module.
async function main() {
const { default: VaultClient, VaultClientV2 } = await import('nanvc');
const vault = new VaultClient('http://127.0.0.1:8200', process.env.VAULT_TOKEN ?? null);
const secretResponse = await vault.read('/secret/my-app/my-secret');
if (!secretResponse.succeeded) {
throw new Error(secretResponse.errorMessage ?? 'Vault read failed');
}
console.log('v1 secret:', secretResponse.apiResponse);
const vaultV2 = new VaultClientV2({
clusterAddress: 'http://127.0.0.1:8200',
authToken: process.env.VAULT_TOKEN ?? null,
});
const secret = await vaultV2.read('secret/my-app/my-secret').unwrap();
console.log('v2 secret:', secret);
}
main().catch((error) => {
console.error(error);
process.exit(1);
});VaultClientV2 and RawVaultClient return a promise-like Result<T>.
This model is intentionally inspired by Rust's Result, adapted for TypeScript and promise-based APIs.
You can await it as a tuple:
const [secret, error] = await vaultV2.read<{ foo: string }>('secret/my-app/my-secret');
if (error) {
console.error(error.message);
} else {
console.log(secret.foo);
}Or use helper methods:
const secret = await vaultV2.secret.kv.v1.read<{ foo: string }>('secret', 'my-app/my-secret').unwrap();
const fallbackSecret = await vaultV2.secret.kv.v1.read<{ foo: string }>('secret', 'my-app/my-secret').unwrapOr({ foo: 'fallback' });
const error = await vaultV2.secret.kv.v1.read('secret', 'my-app/my-secret').intoErr();Available helpers:
.unwrap()returns the success value or throws the error.unwrapOr(defaultValue)returns a fallback value on error.unwrapOrElse(fn)computes a fallback from the error.unwrapErr()returns the error or throws if the result was successful.intoErr()returns the error ornull
VaultClientV2 exposes Vault CLI-style KV shortcuts: read, write, delete, and list.
They default to KV v1 and support KV v2 with { engineVersion: 2 }.
await vault.write('secret/apps/demo', { foo: 'bar' }).unwrap();
const kv1Secret = await vault.read<{ foo: string }>('secret/apps/demo').unwrap();
await vault.write('secret-v2', 'apps/demo', { foo: 'bar' }, { engineVersion: 2 }).unwrap();
const kv2Secret = await vault.read<{ foo: string }>('secret-v2', 'apps/demo', {
engineVersion: 2,
}).unwrap();
console.log(kv1Secret.foo);
console.log(kv2Secret.data.foo);TLS client authentication is optional and disabled by default. Existing constructor calls keep working as-is.
For HTTPS Vault clusters that only need a custom CA, you can pass just ca:
import VaultClient from 'nanvc';
const vault = new VaultClient({
clusterAddress: 'https://vault.local:8200',
authToken: process.env.VAULT_TOKEN ?? null,
tls: {
ca: process.env.VAULT_CA_PEM,
rejectUnauthorized: true,
},
});For HTTPS Vault clusters that require mTLS, use the object-based constructor:
import VaultClient from 'nanvc';
const vault = new VaultClient({
clusterAddress: 'https://vault.local:8200',
authToken: process.env.VAULT_TOKEN ?? null,
apiVersion: 'v1',
tls: {
ca: process.env.VAULT_CA_PEM,
cert: process.env.VAULT_CLIENT_CERT_PEM,
key: process.env.VAULT_CLIENT_KEY_PEM,
passphrase: process.env.VAULT_CLIENT_KEY_PASSPHRASE,
rejectUnauthorized: true,
},
});The tls block supports:
cacertkeypassphraserejectUnauthorized
For the typed v2 client, see the VaultClientV2 API reference.
Original VaultClient methods:
read(path)write(path, payload)update(path, payload)delete(path)list(path)audits()auditHash(path, payload)auths()enableAudit(path, payload)disableAudit(path)enableAuth(path, payload)disableAuth(path)isInitialized()init(payload)mount(path, payload)mounts()policies()addPolicy(name, payload)removePolicy(name)remount(payload)seal()status()unmount(path)unseal(payload)
Every call resolves to a VaultResponse with:
succeededhttpStatusCodeapiResponseerrorMessage
Logging is disabled by default. Set NANVC_LOG_LEVEL to enable request lifecycle logs:
NANVC_LOG_LEVEL=debug node app.jsSupported levels are error, warn, info, and debug. Logs include a local CLI-friendly timestamp, request method, URL, status, and duration, but not tokens or request/response bodies.
Log level prefixes are colorized when output is attached to a TTY. Set NANVC_LOG_NO_COLOR=1 to disable colors or NANVC_LOG_FORCE_COLOR=1 to force them.

Contributions are welcome. Please read the contributing guide before opening an issue or pull request.
This project follows a Code of Conduct to help keep the community welcoming and respectful.
This library covers a focused subset of the Vault API, mainly:
- secret CRUD operations
- initialization and seal management
- mounts/remounts
- auth backends
- audits
- policies
It does not aim to expose the full Vault API surface yet.
secret.kv.v2 currently covers the common read, write, list, and soft-delete workflow. It is not a complete implementation of every KV v2 operation from the Vault OpenAPI specification; use RawVaultClient for unsupported KV v2 endpoints.
MIT