Skip to main content

API Examples

This guide provides practical examples of using PMDaemon's REST and WebSocket APIs in various programming languages and scenarios. From simple process management to complex monitoring dashboards, these examples will help you integrate PMDaemon into your applications.

REST API Examples

JavaScript/Node.js

Basic Process Management

const axios = require('axios');

class PMDaemonClient {
constructor(baseURL = 'http://localhost:9615', apiKey = null) {
const headers = {
'Content-Type': 'application/json'
};

// Add authentication header if API key provided
if (apiKey) {
headers['Authorization'] = `Bearer ${apiKey}`;
}

this.api = axios.create({
baseURL,
timeout: 10000,
headers
});
}

async listProcesses() {
try {
const response = await this.api.get('/api/processes');
return response.data;
} catch (error) {
throw new Error(`Failed to list processes: ${error.message}`);
}
}

async startProcess(nameOrId) {
try {
const response = await this.api.post(`/api/processes/${nameOrId}/start`);
return response.data;
} catch (error) {
throw new Error(`Failed to start process: ${error.message}`);
}
}

async stopProcess(nameOrId) {
try {
const response = await this.api.delete(`/api/processes/${nameOrId}`);
return response.data;
} catch (error) {
throw new Error(`Failed to stop process: ${error.message}`);
}
}

async restartProcess(nameOrId, config = {}) {
try {
const response = await this.api.post(`/api/processes/${nameOrId}/restart`, config);
return response.data;
} catch (error) {
throw new Error(`Failed to restart process: ${error.message}`);
}
}

async getProcessInfo(nameOrId) {
try {
const response = await this.api.get(`/api/processes/${nameOrId}`);
return response.data;
} catch (error) {
throw new Error(`Failed to get process info: ${error.message}`);
}
}

async getSystemMetrics() {
try {
const response = await this.api.get('/api/system');
return response.data;
} catch (error) {
throw new Error(`Failed to get system metrics: ${error.message}`);
}
}

async getProcessLogs(nameOrId, lines = 20, type = 'all') {
try {
const response = await this.api.get(`/api/logs/${nameOrId}`, {
params: { lines, type }
});
return response.data;
} catch (error) {
throw new Error(`Failed to get process logs: ${error.message}`);
}
}
}

// Usage example
async function main() {
// Create client with API key authentication
const client = new PMDaemonClient('http://localhost:9615', 'your-api-key');

try {
// Start an existing process (processes must be created via CLI)
const startResult = await client.startProcess('test-api');
console.log('Process started:', startResult);

// Wait a moment for the process to start
await new Promise(resolve => setTimeout(resolve, 2000));

// Get process information
const processInfo = await client.getProcessInfo('test-api');
console.log('Process info:', processInfo);

// Get system metrics
const systemMetrics = await client.getSystemMetrics();
console.log('System metrics:', systemMetrics);

// Get process logs
const logs = await client.getProcessLogs('test-api', 50);
console.log('Process logs:', logs);

} catch (error) {
console.error('Error:', error.message);
}
}

main();

Deployment Automation

const PMDaemonClient = require('./pmdaemon-client');

class DeploymentManager {
constructor(apiUrl) {
this.client = new PMDaemonClient(apiUrl);
}

async deployService(serviceName, config, healthCheckUrl) {
console.log(`🚀 Deploying ${serviceName}...`);

try {
// Stop existing service if it exists
try {
await this.client.stopProcess(serviceName);
console.log(`✅ Stopped existing ${serviceName}`);
await this.waitForProcessStop(serviceName);
} catch (error) {
// Process might not exist, continue
}

// Start new service
const startResult = await this.client.startProcess({
name: serviceName,
...config
});

if (!startResult.success) {
throw new Error(`Failed to start ${serviceName}: ${startResult.message}`);
}

// Wait for service to be healthy
if (healthCheckUrl) {
await this.waitForHealthy(serviceName, healthCheckUrl);
}

console.log(`${serviceName} deployed successfully`);
return true;

} catch (error) {
console.error(`❌ Failed to deploy ${serviceName}:`, error.message);

// Get logs for debugging
try {
const logs = await this.client.getProcessLogs(serviceName, 20);
console.log('Recent logs:', logs.logs.map(log => log.message).join('\n'));
} catch (logError) {
console.error('Could not retrieve logs:', logError.message);
}

throw error;
}
}

async waitForProcessStop(serviceName, timeout = 30000) {
const startTime = Date.now();

while (Date.now() - startTime < timeout) {
try {
await this.client.getProcessInfo(serviceName);
await new Promise(resolve => setTimeout(resolve, 1000));
} catch (error) {
// Process not found, it's stopped
return;
}
}

throw new Error(`Process ${serviceName} did not stop within ${timeout}ms`);
}

async waitForHealthy(serviceName, healthCheckUrl, timeout = 60000) {
const startTime = Date.now();

while (Date.now() - startTime < timeout) {
try {
const processInfo = await this.client.getProcessInfo(serviceName);

if (processInfo.status === 'online' && processInfo.health === 'healthy') {
return;
}

await new Promise(resolve => setTimeout(resolve, 2000));
} catch (error) {
await new Promise(resolve => setTimeout(resolve, 2000));
}
}

throw new Error(`Service ${serviceName} did not become healthy within ${timeout}ms`);
}

async blueGreenDeploy(serviceName, config, healthCheckUrl) {
const currentColor = await this.getCurrentColor(serviceName);
const newColor = currentColor === 'blue' ? 'green' : 'blue';
const newServiceName = `${serviceName}-${newColor}`;

console.log(`🔄 Blue-green deployment: ${currentColor} -> ${newColor}`);

// Deploy to new color
await this.deployService(newServiceName, config, healthCheckUrl);

// Switch traffic (this would integrate with your load balancer)
console.log(`🔀 Switching traffic to ${newColor}`);

// Wait for traffic to drain
await new Promise(resolve => setTimeout(resolve, 10000));

// Stop old color
if (currentColor) {
const oldServiceName = `${serviceName}-${currentColor}`;
await this.client.stopProcess(oldServiceName);
console.log(`🛑 Stopped old deployment: ${oldServiceName}`);
}

console.log(`✅ Blue-green deployment complete`);
}

async getCurrentColor(serviceName) {
try {
const processes = await this.client.listProcesses();
const blueExists = processes.processes.some(p => p.name === `${serviceName}-blue`);
const greenExists = processes.processes.some(p => p.name === `${serviceName}-green`);

if (blueExists) return 'blue';
if (greenExists) return 'green';
return null;
} catch (error) {
return null;
}
}
}

// Usage
async function deployExample() {
const deployer = new DeploymentManager('http://localhost:9615');

const serviceConfig = {
script: 'node',
args: ['dist/server.js'],
instances: 2,
port: '3000-3001',
env: {
NODE_ENV: 'production',
DATABASE_URL: 'postgres://localhost/myapp'
},
health_check: {
check_type: 'http',
url: 'http://localhost:3000/health',
enabled: true
}
};

await deployer.blueGreenDeploy('web-api', serviceConfig, 'http://localhost:3000/health');
}

deployExample().catch(console.error);

Python

Process Management with Error Handling

import requests
import time
import json
from typing import Dict, List, Optional

class PMDaemonClient:
def __init__(self, base_url: str = "http://localhost:9615"):
self.base_url = base_url.rstrip('/')
self.session = requests.Session()
self.session.headers.update({
'Content-Type': 'application/json'
})

def _request(self, method: str, endpoint: str, **kwargs) -> Dict:
url = f"{self.base_url}{endpoint}"
try:
response = self.session.request(method, url, **kwargs)
response.raise_for_status()
return response.json()
except requests.exceptions.RequestException as e:
raise Exception(f"API request failed: {e}")

def list_processes(self) -> Dict:
return self._request('GET', '/api/processes')

def start_process(self, config: Dict) -> Dict:
return self._request('POST', '/api/processes', json=config)

def stop_process(self, name_or_id: str) -> Dict:
return self._request('DELETE', f'/api/processes/{name_or_id}')

def restart_process(self, name_or_id: str, config: Optional[Dict] = None) -> Dict:
return self._request('POST', f'/api/processes/{name_or_id}/restart',
json=config or {})

def get_process_info(self, name_or_id: str) -> Dict:
return self._request('GET', f'/api/processes/{name_or_id}')

def get_system_metrics(self) -> Dict:
return self._request('GET', '/api/system')

def get_process_logs(self, name_or_id: str, lines: int = 20,
log_type: str = 'all') -> Dict:
params = {'lines': lines, 'type': log_type}
return self._request('GET', f'/api/logs/{name_or_id}', params=params)

def wait_for_process_status(self, name_or_id: str, expected_status: str,
timeout: int = 30) -> bool:
"""Wait for a process to reach a specific status"""
start_time = time.time()

while time.time() - start_time < timeout:
try:
info = self.get_process_info(name_or_id)
if info.get('status') == expected_status:
return True
except Exception:
pass
time.sleep(1)

return False

def wait_for_healthy(self, name_or_id: str, timeout: int = 60) -> bool:
"""Wait for a process to become healthy"""
start_time = time.time()

while time.time() - start_time < timeout:
try:
info = self.get_process_info(name_or_id)
if (info.get('status') == 'online' and
info.get('health') == 'healthy'):
return True
except Exception:
pass
time.sleep(2)

return False

class ProcessMonitor:
def __init__(self, client: PMDaemonClient):
self.client = client

def check_process_health(self, process_name: str) -> Dict:
"""Check the health of a specific process"""
try:
info = self.client.get_process_info(process_name)

health_status = {
'name': process_name,
'status': info.get('status'),
'health': info.get('health'),
'cpu': info.get('cpu'),
'memory': info.get('memory'),
'uptime': info.get('uptime'),
'restarts': info.get('restarts'),
'healthy': info.get('status') == 'online' and info.get('health') == 'healthy'
}

return health_status

except Exception as e:
return {
'name': process_name,
'status': 'unknown',
'healthy': False,
'error': str(e)
}

def monitor_processes(self, process_names: List[str]) -> Dict:
"""Monitor multiple processes and return their health status"""
results = {}

for process_name in process_names:
results[process_name] = self.check_process_health(process_name)

return results

def restart_unhealthy_processes(self, process_names: List[str]) -> Dict:
"""Restart any unhealthy processes"""
results = {}

for process_name in process_names:
health = self.check_process_health(process_name)

if not health.get('healthy', False):
print(f"🔄 Restarting unhealthy process: {process_name}")
try:
restart_result = self.client.restart_process(process_name)

# Wait for process to become healthy
if self.client.wait_for_healthy(process_name, timeout=60):
results[process_name] = {'status': 'restarted', 'healthy': True}
print(f"✅ {process_name} restarted successfully")
else:
results[process_name] = {'status': 'restart_failed', 'healthy': False}
print(f"❌ {process_name} restart failed")

except Exception as e:
results[process_name] = {'status': 'error', 'error': str(e)}
print(f"❌ Error restarting {process_name}: {e}")
else:
results[process_name] = {'status': 'healthy', 'healthy': True}

return results

# Usage example
def main():
client = PMDaemonClient()
monitor = ProcessMonitor(client)

# Deploy a new service
service_config = {
'name': 'python-api',
'script': 'python',
'args': ['-m', 'uvicorn', 'main:app', '--host', '0.0.0.0', '--port', '8000'],
'port': '8000',
'env': {
'PYTHONPATH': '/app',
'DATABASE_URL': 'postgres://localhost/myapp'
},
'health_check': {
'check_type': 'http',
'url': 'http://localhost:8000/health',
'timeout': 10,
'interval': 30,
'enabled': True
}
}

try:
# Start the service
print("🚀 Starting Python API service...")
result = client.start_process(service_config)
print(f"Start result: {result}")

# Wait for it to become healthy
if client.wait_for_healthy('python-api', timeout=60):
print("✅ Service is healthy")
else:
print("❌ Service failed to become healthy")
logs = client.get_process_logs('python-api', lines=20)
print("Recent logs:")
for log in logs.get('logs', []):
print(f" {log.get('message', '')}")

# Monitor the service
health_status = monitor.check_process_health('python-api')
print(f"Health status: {health_status}")

except Exception as e:
print(f"Error: {e}")

if __name__ == "__main__":
main()

Go

Simple Process Management

package main

import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
"time"
)

type PMDaemonClient struct {
BaseURL string
Client *http.Client
}

type ProcessConfig struct {
Name string `json:"name"`
Script string `json:"script"`
Args []string `json:"args,omitempty"`
Port string `json:"port,omitempty"`
Instances int `json:"instances,omitempty"`
Env map[string]string `json:"env,omitempty"`
}

type ProcessInfo struct {
ID int `json:"id"`
Name string `json:"name"`
Status string `json:"status"`
PID int `json:"pid"`
Port int `json:"port"`
CPU float64 `json:"cpu"`
Memory int64 `json:"memory"`
Uptime int64 `json:"uptime"`
Restarts int `json:"restarts"`
Health string `json:"health"`
}

type ProcessListResponse struct {
Processes []ProcessInfo `json:"processes"`
Total int `json:"total"`
}

func NewPMDaemonClient(baseURL string) *PMDaemonClient {
return &PMDaemonClient{
BaseURL: baseURL,
Client: &http.Client{
Timeout: 30 * time.Second,
},
}
}

func (c *PMDaemonClient) request(method, endpoint string, body interface{}) (*http.Response, error) {
var reqBody io.Reader

if body != nil {
jsonData, err := json.Marshal(body)
if err != nil {
return nil, err
}
reqBody = bytes.NewBuffer(jsonData)
}

req, err := http.NewRequest(method, c.BaseURL+endpoint, reqBody)
if err != nil {
return nil, err
}

req.Header.Set("Content-Type", "application/json")

return c.Client.Do(req)
}

func (c *PMDaemonClient) ListProcesses() (*ProcessListResponse, error) {
resp, err := c.request("GET", "/api/processes", nil)
if err != nil {
return nil, err
}
defer resp.Body.Close()

var result ProcessListResponse
if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
return nil, err
}

return &result, nil
}

func (c *PMDaemonClient) StartProcess(config ProcessConfig) error {
resp, err := c.request("POST", "/api/processes", config)
if err != nil {
return err
}
defer resp.Body.Close()

if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusCreated {
return fmt.Errorf("failed to start process: status %d", resp.StatusCode)
}

return nil
}

func (c *PMDaemonClient) StopProcess(nameOrID string) error {
resp, err := c.request("DELETE", "/api/processes/"+nameOrID, nil)
if err != nil {
return err
}
defer resp.Body.Close()

if resp.StatusCode != http.StatusOK {
return fmt.Errorf("failed to stop process: status %d", resp.StatusCode)
}

return nil
}

func (c *PMDaemonClient) GetProcessInfo(nameOrID string) (*ProcessInfo, error) {
resp, err := c.request("GET", "/api/processes/"+nameOrID, nil)
if err != nil {
return nil, err
}
defer resp.Body.Close()

var result ProcessInfo
if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
return nil, err
}

return &result, nil
}

func (c *PMDaemonClient) WaitForHealthy(nameOrID string, timeout time.Duration) error {
start := time.Now()

for time.Since(start) < timeout {
info, err := c.GetProcessInfo(nameOrID)
if err == nil && info.Status == "online" && info.Health == "healthy" {
return nil
}

time.Sleep(2 * time.Second)
}

return fmt.Errorf("process %s did not become healthy within %v", nameOrID, timeout)
}

func main() {
client := NewPMDaemonClient("http://localhost:9615")

// Start a new process
config := ProcessConfig{
Name: "go-api",
Script: "go",
Args: []string{"run", "main.go"},
Port: "8080",
Env: map[string]string{
"GO_ENV": "production",
},
}

fmt.Println("🚀 Starting Go API service...")
if err := client.StartProcess(config); err != nil {
fmt.Printf("❌ Failed to start process: %v\n", err)
return
}

// Wait for it to become healthy
fmt.Println("⏳ Waiting for service to become healthy...")
if err := client.WaitForHealthy("go-api", 60*time.Second); err != nil {
fmt.Printf("❌ Service failed to become healthy: %v\n", err)
return
}

fmt.Println("✅ Service is healthy!")

// List all processes
processes, err := client.ListProcesses()
if err != nil {
fmt.Printf("❌ Failed to list processes: %v\n", err)
return
}

fmt.Printf("📋 Found %d processes:\n", processes.Total)
for _, proc := range processes.Processes {
fmt.Printf(" - %s (PID: %d, Status: %s, Health: %s)\n",
proc.Name, proc.PID, proc.Status, proc.Health)
}
}

WebSocket API Examples

Real-time Dashboard

// dashboard.js
class PMDaemonDashboard {
constructor(wsUrl = 'ws://localhost:9615/ws') {
this.wsUrl = wsUrl;
this.ws = null;
this.processes = new Map();
this.systemMetrics = {};
}

connect() {
this.ws = new WebSocket(this.wsUrl);

this.ws.onopen = () => {
console.log('Connected to PMDaemon WebSocket');
this.subscribe();
};

this.ws.onmessage = (event) => {
const message = JSON.parse(event.data);
this.handleMessage(message);
};

this.ws.onclose = () => {
console.log('WebSocket connection closed');
// Attempt to reconnect after 5 seconds
setTimeout(() => this.connect(), 5000);
};

this.ws.onerror = (error) => {
console.error('WebSocket error:', error);
};
}

subscribe() {
this.send('subscribe', {
events: ['process_status', 'process_metrics', 'system_metrics', 'health_update'],
interval: 1000
});
}

send(type, data) {
if (this.ws && this.ws.readyState === WebSocket.OPEN) {
this.ws.send(JSON.stringify({ type, data }));
}
}

handleMessage(message) {
switch (message.type) {
case 'process_status':
this.updateProcessStatus(message.data);
break;
case 'process_metrics':
this.updateProcessMetrics(message.data);
break;
case 'system_metrics':
this.updateSystemMetrics(message.data);
break;
case 'health_update':
this.updateProcessHealth(message.data);
break;
}
}

updateProcessStatus(data) {
const process = this.processes.get(data.name) || {};
process.status = data.status;
process.pid = data.pid;
process.port = data.port;
this.processes.set(data.name, process);
this.renderProcesses();
}

updateProcessMetrics(data) {
data.processes.forEach(proc => {
const process = this.processes.get(proc.name) || {};
Object.assign(process, proc);
this.processes.set(proc.name, process);
});
this.renderProcesses();
}

updateSystemMetrics(data) {
this.systemMetrics = data;
this.renderSystemMetrics();
}

updateProcessHealth(data) {
const process = this.processes.get(data.process) || {};
process.health = data.status;
this.processes.set(data.process, process);
this.renderProcesses();
}

renderProcesses() {
const container = document.getElementById('processes');
container.innerHTML = '';

Array.from(this.processes.values()).forEach(proc => {
const card = document.createElement('div');
card.className = `process-card ${proc.status} ${proc.health}`;

const nameEl = document.createElement('h3');
nameEl.textContent = proc.name;
card.appendChild(nameEl);

const statusEl = document.createElement('div');
statusEl.className = 'status';
statusEl.textContent = `Status: ${proc.status}`;
card.appendChild(statusEl);

// Continue with other elements...
container.appendChild(card);
});
<div class="metrics">
<span>CPU: ${proc.cpu?.toFixed(1) || 0}%</span>
<span>Memory: ${proc.memory ? (proc.memory / 1024 / 1024).toFixed(1) : 0}MB</span>
<span>PID: ${proc.pid || 'N/A'}</span>
<span>Port: ${proc.port || 'N/A'}</span>
</div>
<div class="actions">
<button onclick="dashboard.restartProcess('${proc.name}')">Restart</button>
<button onclick="dashboard.stopProcess('${proc.name}')">Stop</button>
</div>
</div>
`).join('');
}

renderSystemMetrics() {
const container = document.getElementById('system-metrics');
const metrics = this.systemMetrics;

container.innerHTML = `
<div class="metric">
<h4>CPU Usage</h4>
<div class="value">${metrics.cpu?.usage?.toFixed(1) || 0}%</div>
</div>
<div class="metric">
<h4>Memory Usage</h4>
<div class="value">${metrics.memory?.usage_percent?.toFixed(1) || 0}%</div>
</div>
<div class="metric">
<h4>Load Average</h4>
<div class="value">${metrics.load?.one?.toFixed(2) || 0}</div>
</div>
`;
}

restartProcess(name) {
this.send('command', {
action: 'restart',
process: name
});
}

stopProcess(name) {
this.send('command', {
action: 'stop',
process: name
});
}
}

// Initialize dashboard
const dashboard = new PMDaemonDashboard();
dashboard.connect();

Next Steps