Skip to content

Getting Started

kurok edited this page Aug 31, 2026 · 3 revisions

Getting Started

Requirements

Node.js 18 or newer (CI runs the suite on 18, 20, 22, 24)
Vault Any version exposing the v1 HTTP API; KV v1 and KV v2 both supported
Optional peer config >=1 <4, only if you use fillNodeConfig()
npm install node-vault-client

The package ships CommonJS (main: src/VaultClient.js), so both require() and import work.

Booting a client

VaultClient.boot(name, options) creates a named singleton. Calling it again with the same name returns the same instance, which is what you want for a long-lived process — one token, one renewal timer.

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

VaultClient.boot('main', {
    api:  { url: 'https://vault.example.com:8200/' },
    auth: { type: 'appRole', config: { role_id: '637c065f-...', secret_id: '...' } },
});

// anywhere else in the process
const vault = VaultClient.get('main');
Static method Purpose
VaultClient.boot(name, options) Create (or fetch) the named instance
VaultClient.get(name) Fetch an already-booted instance
VaultClient.clear(name) Drop one instance, or all of them when called with no name

Options

{
    api: {
        url: 'https://vault.example.com:8200/',  // required
        apiVersion: 'v1',                        // default 'v1'
        namespace: 'team-a',                     // Vault Enterprise namespace
        engines: { secret: 2, legacy: 1 },       // static mount-version map
        kv: { autoDetect: false },               // ask Vault for each mount's version
        requestOptions: {},                      // merged into every HTTP request
    },
    auth: {
        type: 'token',                           // 'token' | 'appRole' | 'iam' | 'kubernetes' | 'jwt'
        mount: 'token',                          // default varies per type
        renewal: true,                           // false = never renew in the background
        renewalFraction: 0.5,                    // when to renew, as a fraction of remaining TTL
        renewalIncrement: undefined,             // seconds of extra TTL to request
        config: { /* backend specific, below */ },
    },
    logger: false,                               // false disables logging; default is console
}

logger accepts any object implementing error, warn, info, debug and trace. Anything missing a method falls back to console, so pass a complete logger or false.

Auth backends

Token

For local development and CI, where a token already exists.

auth: { type: 'token', config: { token: process.env.VAULT_TOKEN } }

AppRole

The usual choice for VMs and non-cloud workloads.

auth: {
    type: 'appRole',
    config: {
        role_id: '637c065f-c644-5e12-d3d1-e9fa4363af61',
        secret_id: '...',   // omit for a bind-cidr / secret-id-less role
    },
}

AWS IAM

For EC2, ECS and Lambda. With no credentials block the client uses the AWS default provider chain (fromNodeProviderChain()), so instance/task/execution roles work with no secrets in your config at all.

auth: {
    type: 'iam',
    config: {
        role: 'my-app',
        region: 'us-east-1',                          // optional
        iam_server_id_header_value: 'vault.example.com', // optional, if the role requires it
        // credentials: { accessKeyId, secretAccessKey } // optional; omit to use the AWS chain
    },
}

Kubernetes

For pods. The service-account JWT is read from disk on each login, so token rotation by the kubelet is picked up.

auth: {
    type: 'kubernetes',
    config: {
        role: 'my-app',
        tokenPath: '/var/run/secrets/kubernetes.io/serviceaccount/token', // default
    },
}

JWT

Availability: the JWT backend is on master and ships in the next release; it is not in npm 2.1.2.

For anything that can hand you a JWT: CI jobs (GitHub Actions OIDC), a cloud metadata endpoint, a SPIFFE/SPIRE workload. Exactly one of jwt / jwtPath / jwtProvider supplies the token; role is optional and falls back to the mount's default_role when omitted.

auth: {
    type: 'jwt',
    mount: 'jwt',                                   // default
    config: {
        role: 'my-app',                             // optional; falls back to the mount's default_role
        jwt: process.env.CI_JOB_JWT,                // a literal token string, OR:
        // jwtPath: '/path/to/token',                // re-read from disk on every login, OR:
        // jwtProvider: () => core.getIDToken('vault'), // called fresh at every login
    },
}

See Recipe 14 for the GitHub Actions OIDC case, and note that this client only implements the non-interactive jwt login — the browser-redirect oidc flow is out of scope for a headless service client.

Token renewal

Availability: the three renewal* keys are not in npm 2.1.2 yet — they land with the next release.

When Vault issues a renewable token the client renews it in the background — at half its remaining lifetime, for as long as the client lives. That is the default and suits long-running services. Three optional keys on auth change it:

key default meaning
renewal true false arms no timer at all: the token is used until it expires, then the next call re-authenticates
renewalFraction 0.5 Fraction of the remaining lifetime to wait before renewing, in (0, 1)
renewalIncrement (unset) Seconds of extra TTL to request, sent as increment to auth/token/renew-self

They sit on auth, beside type — not inside auth.config, which is the backend's own credential bag:

auth: {
    type: 'kubernetes',
    renewal: false,
    config: { role: 'my-app' },
}

Before turning renewal off, check your backend can get a fresh credential unaided:

backend on expiry with renewal: false
kubernetes, iam, jwt with jwtPath/jwtProvider clean re-login — the intended case
appRole replays the same secret_id; fatal if the role uses secret_id_num_uses=1
jwt with a literal jwt replays the same JWT, so it works only until the IdP token expires
token cannot re-authenticate at all — raises AuthTokenExpiredError from then on, permanently

Two more things worth knowing: a login that can no longer succeed is retried on every call with no backoff, and letting a token expire also revokes the leases it created — this client renews only the auth token, so dynamic secrets (database/creds/*) whose lease outlives the auth token depend on renewal being on. See recipe 15.

Reading a secret

read() resolves to a Lease, not a bare object:

const lease = await vault.read('secret/app');

lease.getData();              // { DB_PASSWORD: 's3cret', API_KEY: 'k' }
lease.getValue('DB_PASSWORD'); // 's3cret'
lease.isRenewable();           // false
lease.getMetadata();           // { version: 3 }  — KV v2 only

Writing, updating, deleting

await vault.write('secret/app', { DB_PASSWORD: 'new' });   // full write
await vault.update('secret/app', { API_KEY: 'k2' });        // merge-patch: only this key changes
await vault.delete('secret/app');
await vault.list('secret');

update() issues an HTTP PATCH with Content-Type: application/merge-patch+json, so the other keys in the secret are left alone. It requires a Vault that supports merge-patch on KV v2.

Shutting down

The renewal timer keeps the event loop alive. Close the client when your process is winding down:

vault.close();

Next: Recipes & Use Cases.

Clone this wiki locally