pve-client
    Preparing search index...

    pve-client

    Proxmox

    pve-client

    Full-coverage TypeScript client for the Proxmox VE REST API

    npm version JSR License codecov Node.js

    Docs · npm · JSR · Issues · Discussions


    pve-client is a TypeScript-first API client covering all 675 endpoints of the official Proxmox VE REST API — typed end-to-end so your editor knows every path, parameter, and return shape. No hand-rolled fetch calls, no guessing field names.

    import { Client } from "pve-client";

    const client = new Client({ baseUrl: "https://pve.example.com:8006", apiToken: "..." });

    const nodes = await client.api.nodes.list();
    // ^? { node: string; status: "online" | "offline" | ...; ... }[]


    675 typed endpoints Every Proxmox VE API path — GET, POST, PUT, DELETE — is typed with parameter and return types generated from the official spec
    Two auth methods API token (recommended) or username/password ticket flow
    Autocomplete everywhere Path params, query strings, body fields, and return shapes all resolve in your IDE
    Response unwrapping Proxmox wraps responses in { data: ... } — this client strips it automatically
    Task polling Built-in client.task.listen() and client.task.wait() for async Proxmox tasks
    Live resource events Subscribe to cluster resource and task streams via client.events
    ESM + CJS Dual-format build works in any modern Node.js project
    Node.js ≥ 18 Uses native fetch; bring your own HTTPS agent for self-signed certs

    # npm
    npm install pve-client

    # yarn
    yarn add pve-client

    # pnpm
    pnpm add pve-client

    JSR (Deno / JSR-compatible runtimes):

    deno add @sourceregistry/proxmox
    # or
    npx jsr add @sourceregistry/proxmox

    import { Client } from "pve-client";
    import { Agent } from "node:https";

    const client = new Client({
    baseUrl: "https://pve.example.com:8006",
    apiToken: "PVEAPIToken=root@pam!mytoken=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",

    // required only for self-signed certificates
    agent: new Agent({ rejectUnauthorized: false }),
    });

    // list nodes
    const nodes = await client.api.nodes.list();

    // get cluster status
    const status = await client.api.cluster.status();

    // get VMs on a node
    const vms = await client.api.nodes.get("pve").qemu.list();

    Create a token in the Proxmox web UI under Datacenter → Permissions → API Tokens, then:

    const client = new Client({
    baseUrl: "https://pve.example.com:8006",
    apiToken: "PVEAPIToken=root@pam!mytoken=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
    // shorthand also works:
    // apiToken: "root@pam!mytoken=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
    });
    const client = new Client({
    baseUrl: "https://pve.example.com:8006",
    username: "root",
    password: "your-password",
    realm: "pam", // defaults to "pam"
    });

    await client.login(); // exchanges credentials for a ticket + CSRF token

    Note: Some Proxmox operations (VM console, terminal access) require ticket-based auth and are unavailable to API tokens regardless of permissions.

    PVE_BASE_URL=https://pve.example.com:8006
    PVE_API_TOKEN=PVEAPIToken=root@pam!mytoken=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
    
    import "dotenv/config";
    import { Client } from "pve-client";

    const client = new Client({
    baseUrl: process.env.PVE_BASE_URL!,
    apiToken: process.env.PVE_API_TOKEN!,
    });

    The client exposes all 675 Proxmox endpoints through six typed modules:

    Module Path prefix Covers
    client.api.access /access Users, groups, roles, ACLs, domains, TFA, tickets
    client.api.cluster /cluster Cluster config, HA, replication, firewall, SDN, backup, ACME, resources
    client.api.nodes /nodes Node management, VMs (QEMU), containers (LXC), storage, networking, tasks
    client.api.pools /pools Resource pool management
    client.api.storage /storage Storage configuration
    client.api.version /version API version info

    Every method accepts an object with the following optional keys:

    Key Used for
    $path URL path parameters — usually pre-bound by helper methods like .get("node")
    $query Query string parameters (GET / DELETE)
    $body Request body (POST / PUT — sent as application/x-www-form-urlencoded)
    $headers Extra request headers
    // explicit path/query/body example
    await client.api.access.permissions({
    $query: { path: "/vms/100", userid: "root@pam" },
    });

    await client.request("/nodes/{node}/qemu", "POST", {
    $path: { node: "pve" },
    $body: { vmid: 101, memory: 2048, cores: 2, name: "my-vm" },
    });

    const nodes = await client.api.nodes.list();

    for (const node of nodes) {
    console.log(`${node.node}: ${node.status} (${node.uptime}s uptime)`);
    }
    const node = client.api.nodes.get("pve");

    // list VMs
    const vms = await node.qemu.list();

    // start a VM
    await node.qemu.vmid(100).status.start();

    // stop a VM
    await node.qemu.vmid(100).status.stop();

    // clone a VM
    await node.qemu.vmid(100).clone({ $body: { newid: 200, name: "clone-vm" } });
    const node = client.api.nodes.get("pve");

    const containers = await node.lxc.list();
    await node.lxc.id(200).status.start();
    await node.lxc.id(200).config.get();
    const storage = client.api.nodes.get("pve").storage;

    // list available LXC templates
    const templates = await storage.get("local").content.list({
    $query: { content: "vztmpl" },
    });
    // all VMs across the cluster
    const vms = await client.api.cluster.resources({
    $query: { type: "vm" },
    });

    Proxmox operations return a UPID (task ID). Use client.task to track completion:

    const upid = await client.request("/nodes/{node}/qemu/{vmid}/status/start", "POST", {
    $path: { node: "pve", vmid: 100 },
    });

    const logs = await client.task.wait(upid, (update) => {
    console.log(`[${update.status}]`, update.logs.at(-1));
    });

    console.log("Task completed:", logs);
    const sub = client.task.listen(upid, async (update) => {
    if (update.status === "stopped") console.log("Done:", update.logs);
    if (update.status === "failed") console.error("Failed:", update.logs);
    }, 1000 /* poll interval ms */);

    // cancel early if needed
    sub.stop();

    client.events provides live-polling streams for cluster state:

    const monitor = client.events.resources();

    monitor.on("qemu/100", (resource) => {
    console.log("VM 100 updated:", resource.status);
    });

    monitor.start(5000); // poll every 5 seconds
    const tasks = client.events.tasks();

    tasks.on("task", (task) => {
    console.log("New task:", task.type, task.status);
    });

    tasks.start(3000);
    client.events.stopListening();
    

    Non-2xx responses throw an Error with the HTTP status and response body:

    try {
    await client.api.nodes.list();
    } catch (err) {
    // err.message: "HTTP 401 Unauthorized: ..."
    console.error(err);
    }

    Runnable examples live in the examples/ directory. Copy .env.example to .env and fill in your cluster details.

    npm run example/auth
    
    npm run example/terminal -- 100
    # or set PVE_VMID in .env and omit the argument
    npm run example/terminal

    Requires username/password auth. Press Ctrl-] to disconnect.

    npm run example/tasks
    

    .env reference:

    # required
    PVE_BASE_URL=https://pve.example.com:8006
    
    # token auth (recommended)
    PVE_API_TOKEN=PVEAPIToken=root@pam!tokenid=secret
    
    # OR username/password (required for terminal)
    PVE_USERNAME=root
    PVE_PASSWORD=your-password
    PVE_REALM=pam
    
    # optional
    PVE_VMID=100
    PVE_TASKS_POLL_INTERVAL_MS=2000
    

    git clone https://github.com/AlexanderSlaa/pve-client.git
    cd pve-client
    npm install

    npm run build # compile TypeScript + Vite bundle
    npm test # run tests (Vitest)
    npm run test:coverage # run tests with coverage report
    npm run docs:build # generate TypeDoc HTML docs

    Test suites cover Client authentication, request handling, event polling, task monitoring, and TimerPulledEventEmitter internals.


    Contributions are welcome! Here is how you can help:

    • Bug reports — open an issue with a minimal reproduction
    • Feature requests — start a discussion before opening a PR
    • Pull requests — fork, branch off main, make your change, open a PR
    • Keep commits in Conventional Commits format (feat:, fix:, refactor:, etc.) — releases are automated via semantic-release
    • All new API surface needs a corresponding type in src/api/
    • Run npm test and npm run build before opening a PR
    • Factory files export a single export default function — one thing per file

    The type definitions are generated from the official Proxmox spec in res/apidoc.js. If you find a missing or incorrect endpoint, open an issue referencing the Proxmox API path and the expected parameters/return shape.


    Apache-2.0 © Alexander Slaa