npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

node-vault-client

v2.1.2

Published

A Vault Client implemented in pure javascript for HashiCorp Vault. It supports variety of Auth Backends and performs lease renewal for issued auth token.

Readme

Node.js Vault Client

Build Status Download Status NPM Version License Dependency Status

A Vault Client implemented in pure javascript for HashiCorp Vault. It supports variety of Auth Backends and performs lease renewal for issued auth token.

Install

npm install --save node-vault-client

Requirements

Node.js >= 18 — the client uses the native fetch API.

Example

const VaultClient = require('node-vault-client');

const vaultClient = VaultClient.boot('main', {
    api: { url: 'https://vault.example.com:8200/' },
    auth: { 
        type: 'appRole', // one of: 'appRole' | 'token' | 'iam' | 'kubernetes'
        config: { role_id: '637c065f-c644-5e12-d3d1-e9fa4363af61' } 
    },
});

vaultClient.read('secret/tst').then(lease => {
    console.log(lease.getData()); // read() resolves to a Lease; use getData()/getValue(key)
}).catch(e => console.error(e));

Supported Auth Backends

AWS IAM auth

const vaultClient = VaultClient.boot('main', {
    api: {
        url: 'https://vault.example.com:8200/',
        namespace: 'some_namespace',                   // Optional. X-Vault-Namespace header (canonical location; auth.config.namespace is honored as a legacy fallback)
    },
    auth: {
        type: 'iam',
        mount: 'aws',                                  // Optional. Vault AWS auth mount point ("aws" by default)
        config: {
            role: 'my_iam_role',
            iam_server_id_header_value: 'https://vault.example.com:8200/', // Optional. X-Vault-AWS-IAM-Server-ID header
            region: 'eu-central-1',                     // Optional. AWS STS region (see below)
            credentials: {                             // Optional. Resolved from the AWS provider chain when omitted
                accessKeyId: process.env.AWS_ACCESS_KEY_ID,
                secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
            },
        },
    },
});

region

By default the signed GetCallerIdentity request targets the global STS endpoint sts.amazonaws.com and the SigV4 credential scope is bound to us-east-1. Set config.region to sign against a regional STS endpoint instead — the request is then sent to sts.<region>.amazonaws.com and the signature scope is bound to that region. This is required when Vault's sts_region / sts_endpoint is configured for a non-us-east-1 region (e.g. eu-central-1); otherwise STS rejects the replayed request with SignatureDoesNotMatch — Credential should be scoped to a valid region. Omitting region preserves the previous (global-endpoint) behavior.

AppRole auth

const vaultClient = VaultClient.boot('main', {
    api: { url: 'https://vault.example.com:8200/' },
    auth: {
        type: 'appRole',
        mount: 'approle',                              // Optional. Vault AppRole auth mount point ("approle" by default)
        config: {
            role_id: '637c065f-c644-5e12-d3d1-e9fa4363af61', // Required. RoleID of the AppRole
            secret_id: '...',                          // Optional. Required when bind_secret_id is enabled
        },
    },
});

Token auth

const vaultClient = VaultClient.boot('main', {
    api: { url: 'https://vault.example.com:8200/' },
    auth: {
        type: 'token',
        mount: 'token',                                // Optional. Vault token auth mount point ("token" by default)
        config: {
            token: 's.xxxxxxxxxxxxxxxxxxxxxxxx',       // Required. Vault token
        },
    },
});

Kubernetes auth

const vaultClient = VaultClient.boot('main', {
    api: { url: 'https://vault.example.com:8200/' },
    auth: {
        type: 'kubernetes',
        mount: 'kubernetes',                           // Optional. Vault Kubernetes auth mount point ("kubernetes" by default)
        config: {
            role: 'my_k8s_role',                       // Required. Role configured in the Vault Kubernetes auth backend
            tokenPath: '/var/run/secrets/kubernetes.io/serviceaccount/token', // Optional. Defaults to the in-pod service-account token path
        },
    },
});

API

VaultClient

Return contract: read() and list() resolve to a Lease — use its accessors to extract the secret data. Every other data-plane method (write, delete, update, request and the KV v2 helpers) resolves to the raw parsed Vault response body, which may be empty/undefined for 204 No Content responses.

new VaultClient(options)

Client constructor function.

| Param | Type | Default | Description | | --- | --- | --- | --- | | options | Object | | | | options.api | Object | | | | options.api.url | String | | the url of the vault server | | [options.api.apiVersion] | String | v1 | | | [options.api.requestOptions] | Object | | extra options merged into every HTTP request (see Custom transport) | | [options.api.namespace] | String | | Optional. Vault namespace, sent as the X-Vault-Namespace header on every request — login, token lookup/renewal, and all secret operations — for every auth type. This is the canonical location; auth.config.namespace is still honored for backward compatibility. | | [options.api.kv.autoDetect] | boolean | false | auto-detect the KV version of each mount on first use (see KV v2 & generic backends) | | [options.api.engines] | Object | {} | static mount-to-version map, e.g. { secret: 2, legacy: 1 } (see KV v2 & generic backends) | | options.auth | Object | | | | options.auth.type | String | | | | [options.auth.mount] | String | | Vault auth backend mount point; default varies per method (e.g. "aws" for iam, "approle", "token", "kubernetes") | | options.auth.config | Object | | auth configuration variables | | [options.auth.config.namespace] | String | | Optional. Legacy location for the Vault namespace (see api.namespace). Sent as the X-Vault-Namespace header on every request for every auth type. | | [options.logger] | Object | false | | Logger that supports "error", "info", "warn", "trace", "debug" methods. Uses console by default. Pass false to disable logging. |

Custom transport (proxy / self-signed TLS)

options.api.requestOptions is shallow-merged into every underlying fetch() call, so you can route traffic through a proxy/SOCKS agent or trust a self-signed / internal-CA Vault. Pass an undici dispatcher (request semantics like method and body always win; headers are merged with per-request headers taking precedence):

const { Agent, ProxyAgent } = require('undici');

// Trust an internal/self-signed CA (preferred over disabling verification)
const vaultClient = VaultClient.boot('main', {
    api: {
        url: 'https://vault.internal:8200/',
        requestOptions: {
            dispatcher: new Agent({ connect: { ca: require('fs').readFileSync('/etc/ssl/internal-ca.pem') } }),
        },
    },
    auth: { type: 'token', config: { token: '...' } },
});

// Route through an HTTP proxy / SOCKS agent
const proxied = VaultClient.boot('proxied', {
    api: { url: 'https://vault.example.com:8200/', requestOptions: { dispatcher: new ProxyAgent('http://proxy:8080') } },
    auth: { type: 'token', config: { token: '...' } },
});

For the self-signed-CA case you can also use the process-wide NODE_EXTRA_CA_CERTS=/path/ca.pem env var with no code change. Only disable verification (new Agent({ connect: { rejectUnauthorized: false } })) in throwaway/dev setups — it removes MITM protection.

vaultClient.fillNodeConfig() ⇒ Promise

Populates Vault's values to NPM "config" module

Resolves once the npm config module has been populated from Vault.

Kind: instance method of VaultClient

vaultClient.read(path) ⇒ Promise.<Lease>

Read secret from Vault

Kind: instance method of VaultClient

| Param | Type | Description | | --- | --- | --- | | path | string | path to the secret |

vaultClient.list(path) ⇒ Promise.<Lease>

Retrieves secrets list

Kind: instance method of VaultClient

| Param | Type | Description | | --- | --- | --- | | path | string | path to the secret |

vaultClient.write(path, data) ⇒ Promise.<Object>

Writes data to Vault

Resolves to the raw parsed Vault response body, which may be empty/undefined for 204 No Content responses.

Kind: instance method of VaultClient

| Param | Type | Description | | --- | --- | --- | | path | string | path used to write data | | data | object | data to write |

vaultClient.delete(path) ⇒ Promise.<Object>

Deletes a secret

On KV v2 mounts this sends DELETE to the data/ path, soft-deleting the latest version. On KV v1 / non-KV mounts this sends DELETE to the raw path. Resolves to the raw parsed Vault response body, which may be empty/undefined for 204 No Content responses.

Kind: instance method of VaultClient

| Param | Type | Description | | --- | --- | --- | | path | string | path to the secret |

vaultClient.update(path, data) ⇒ Promise.<Object>

Updates (merge-patches) a KV v2 secret

Sends PATCH with Content-Type: application/merge-patch+json, merging data into the existing secret without overwriting keys that are not listed. KV v2 merge-patch operation — KV v1 mounts do not support PATCH and Vault returns 405 there. Resolves to the raw parsed Vault response body.

Kind: instance method of VaultClient

| Param | Type | Description | | --- | --- | --- | | path | string | path to the secret | | data | object | keys to merge into the existing secret |

vaultClient.request(method, path, [data]) ⇒ Promise.<Object>

Raw request — escape hatch for any Vault backend

Sends the literal API path with no KV path rewriting and no response unwrapping, and resolves to the parsed response body. Use it for non-KV backends (e.g. Transit) or when you have already constructed the complete Vault API path.

Kind: instance method of VaultClient

| Param | Type | Description | | --- | --- | --- | | method | string | HTTP method (e.g. GET, POST) | | path | string | literal API path, sent as-is | | [data] | object | request body |

vaultClient.deleteVersions(path, versions) ⇒ Promise.<Object>

Soft-deletes specific versions of a KV v2 secret

KV v2 only — rejects with UnsupportedOperationError on non-v2 mounts. Resolves to the raw parsed Vault response body.

Kind: instance method of VaultClient

| Param | Type | Description | | --- | --- | --- | | path | string | path to the secret | | versions | Array.<number> | version numbers to soft-delete |

vaultClient.undeleteVersions(path, versions) ⇒ Promise.<Object>

Undeletes (restores) soft-deleted versions of a KV v2 secret

KV v2 only — rejects with UnsupportedOperationError on non-v2 mounts. Resolves to the raw parsed Vault response body.

Kind: instance method of VaultClient

| Param | Type | Description | | --- | --- | --- | | path | string | path to the secret | | versions | Array.<number> | version numbers to restore |

vaultClient.destroyVersions(path, versions) ⇒ Promise.<Object>

Permanently destroys specific versions of a KV v2 secret

The destroyed version data cannot be recovered. KV v2 only — rejects with UnsupportedOperationError on non-v2 mounts. Resolves to the raw parsed Vault response body.

Kind: instance method of VaultClient

| Param | Type | Description | | --- | --- | --- | | path | string | path to the secret | | versions | Array.<number> | version numbers to destroy |

vaultClient.readMetadata(path) ⇒ Promise.<Object>

Reads KV v2 metadata for a secret

Resolves to the metadata document (current_version, the versions map, timestamps, etc.). KV v2 only — rejects with UnsupportedOperationError on non-v2 mounts.

Kind: instance method of VaultClient

| Param | Type | Description | | --- | --- | --- | | path | string | path to the secret |

vaultClient.deleteMetadata(path) ⇒ Promise.<Object>

Deletes all metadata and version history for a KV v2 secret (permanent)

KV v2 only — rejects with UnsupportedOperationError on non-v2 mounts. Resolves to the raw parsed Vault response body, which may be empty/undefined for 204 No Content responses.

Kind: instance method of VaultClient

| Param | Type | Description | | --- | --- | --- | | path | string | path to the secret |

vaultClient.close()

Release resources held by this client.

This client performs lease renewal for renewable auth tokens by arming a background timer. That timer keeps the Node.js event loop alive, so a short-lived script (e.g. a one-off read) never exits on its own. Call close() once you are done with the client to cancel the timer and let the process exit. It is null-safe and safe to call multiple times. The client may still be used afterwards — the next operation that fetches a renewable token will arm a new refresh timer.

const vaultClient = VaultClient.boot('main', { /* ... */ });
const secret = await vaultClient.read('secret/tst');
console.log(secret);
vaultClient.close(); // process can now exit

Kind: instance method of VaultClient

Lease

The object returned by read() and list() (they resolve to Promise<Lease>). Use its accessors to extract the secret data:

  • getValue(key)String — value for a single key. Throws Requested key does not exist when the key is absent.
  • getData()Object — a deep-cloned copy of the whole secret data object.
  • isRenewable()boolean — whether the underlying lease is renewable.

VaultClient.boot(name, [options]) ⇒ VaultClient

Boot an instance of Vault

The instance will be stored in a local hash. Calling Vault.boot multiple times with the same name will return the same instance.

Kind: static method of VaultClient
Returns: VaultClient

| Param | Type | Description | | --- | --- | --- | | name | String | Vault instance name | | [options] | Object | options for Vault#constructor. |

VaultClient.get(name) ⇒ VaultClient

Get an instance of Vault

The instance will be stored in a local hash. Calling Vault.pop multiple times with the same name will return the same instance.

Kind: static method of VaultClient
Returns: VaultClient

| Param | Type | Description | | --- | --- | --- | | name | String | Vault instance name |

VaultClient.clear([name])

Clear named Vault instance

If no name passed all named instances will be cleared.

Kind: static method of VaultClient

| Param | Type | Description | | --- | --- | --- | | [name] | String | Vault instance name, all instances will be cleared if no name were passed |

KV v2 & generic backends

Overview

By default the client behaves exactly as before (KV v1 / raw passthrough). To enable transparent KV v2 support set api.kv.autoDetect: true or supply a static api.engines map. Either option activates path-rewriting and response-unwrapping; callers do not need to know the engine version.

Configuration options

| Option | Type | Default | Description | |---|---|---|---| | api.kv.autoDetect | boolean | false | Auto-detect the KV version of each mount on first use via GET sys/internal/ui/mounts/<path>. | | api.engines | Object | {} | Static mount-to-version map, e.g. { secret: 2, legacy: 1 }. Overrides detection; use this when the token lacks permission on sys/internal/ui/mounts. |

Both options can be combined: engines acts as an override — matching mounts skip detection while unmatched mounts are auto-detected (when autoDetect: true).

Auto-detect example

const client = VaultClient.boot('main', {
    api: {
        url: 'https://vault.example.com:8200/',
        kv: { autoDetect: true },
    },
    auth: { type: 'token', config: { token: '...' } },
});

// Works transparently on both KV v1 and KV v2 mounts
const lease = await client.read('secret/my-app/config');
console.log(lease.getData());         // the secret object
console.log(lease.getMetadata());     // KV v2 version metadata (undefined on v1)

Static engines override example

const client = VaultClient.boot('main', {
    api: {
        url: 'https://vault.example.com:8200/',
        engines: { secret: 2, legacy: 1 },
    },
    auth: { type: 'token', config: { token: '...' } },
});

KV v2-specific methods

These methods require a KV v2 mount and throw UnsupportedOperationError on v1 / non-KV mounts.

// Soft-delete specific versions
await client.deleteVersions('secret/foo', [1, 2]);

// Restore soft-deleted versions
await client.undeleteVersions('secret/foo', [1]);

// Permanently destroy versions
await client.destroyVersions('secret/foo', [1, 2]);

// Read version metadata (current_version, versions map, etc.)
const meta = await client.readMetadata('secret/foo');

// Delete all metadata and version history (permanent)
await client.deleteMetadata('secret/foo');

update() — merge-patch

// PATCH a subset of keys without overwriting others (KV v2)
await client.update('secret/foo', { password: 'new-value' });
// Sends PATCH secret/data/foo with Content-Type: application/merge-patch+json

delete()

// Soft-delete the latest version on KV v2; DELETE on v1/passthrough
await client.delete('secret/foo');

request() — raw escape hatch

For any Vault backend that does not benefit from KV path rewriting use request(). It sends the literal path with no rewriting or response normalisation and returns the parsed body directly.

// Encrypt with Transit engine — path must not be rewritten
const result = await client.request('POST', 'transit/encrypt/my-key', {
    plaintext: Buffer.from('hello').toString('base64'),
});
console.log(result.data.ciphertext);

Lease.getMetadata()

getMetadata() is additive — existing code is unaffected.

const lease = await client.read('secret/my-app/db');
lease.getData();     // the secret values
lease.getMetadata(); // { version, created_time, deletion_time, destroyed, custom_metadata }
                     // undefined on KV v1 / passthrough mounts

Path requirements when autoDetect / engines are active

When autoDetect: true or api.engines is set, the client rewrites logical paths to the correct KV v2 API paths automatically (e.g. secret/foosecret/data/foo for reads). Callers must pass logical paths — do not include the internal KV v2 segments (data/, metadata/, delete/, undelete/, destroy/) in the path argument:

// Correct — logical path only
await client.read('secret/my-app/config');

// Wrong — double-rewrite: 'secret/data/foo' becomes 'secret/data/data/foo' on the wire
await client.read('secret/data/foo');

If you need to send a fully-literal Vault API path (e.g. when working with non-KV backends or when you have already constructed the complete path), use request() which bypasses all path rewriting:

// Literal path, no rewriting
await client.request('GET', 'secret/data/foo');

Mount detection caching

  • Each canonical mount is detected at most once per VaultClient instance.
  • Concurrent first-touch requests for the same mount share a single in-flight detection promise.
  • The detection endpoint used is GET sys/internal/ui/mounts/<path> (readable by any authenticated token).
  • When the token lacks permission on that endpoint, set api.engines to skip detection.

Error classes

| Class | When thrown | |---|---| | UnsupportedOperationError | A v2-only method (deleteVersions, undeleteVersions, destroyVersions, readMetadata, deleteMetadata) is called against a non-v2 mount. | | VaultError | Mount detection fails (e.g. permission denied) and no api.engines override was provided. |

Contributing

Contributions are welcome! Please read the contributing guide to get started, and note that this project requires a DCO sign-off on every commit.

Getting help

Not sure where to start? See SUPPORT.md.

Code of Conduct

This project adheres to the Contributor Covenant Code of Conduct.

Security

To report a security vulnerability, please follow our Security Policy.

License

Licensed under the Apache License 2.0.