Skip to main content

Best Practices

Token Usage

All requests to the PWR API require an OAuth 2.0 access token passed in the Authorization header. Reuse the same token until it expires—do not request a new token for every request.

✅ DO THIS - Cache and reuse tokens:

let accessToken = null;
let tokenExpiration = null;

async function getValidToken() {
if (accessToken && Date.now() < tokenExpiration) {
return accessToken; // Reuse existing token
}
// Request new token only when expired
const response = await getNewToken(); // Your token endpoint
accessToken = response.access_token;
tokenExpiration = Date.now() + (response.expires_in * 1000);
return accessToken;
}

// Use same token for multiple requests
const token = await getValidToken();
await fetch('/v1/sites', { headers: { 'Authorization': `Bearer ${token}` } });
await fetch('/v1/alerts', { headers: { 'Authorization': `Bearer ${token}` } });

❌ AVOID:

// Don't request a new token for every request
const token1 = await requestNewToken(); // ❌
await fetch('/v1/sites', { headers: { 'Authorization': `Bearer ${token1}` } });
const token2 = await requestNewToken(); // ❌
await fetch('/v1/alerts', { headers: { 'Authorization': `Bearer ${token2}` } });

Client Implementation Requirements

To ensure your client handles API changes gracefully, follow these best practices:

1. Handle Unknown Response Fields

Always ignore unknown fields in response objects. The API may add new fields at any time, and your implementation must not break.

JavaScript/TypeScript Example:

// GET /v1/sites/{siteId} returns site details
const response = await fetch('/v1/sites/{EXAMPLE_SITE_ID}').then(r => r.json());

// Extract only the fields your application needs
const siteInfo = {
siteId: response.siteId,
siteName: response.siteName,
timezone: response.timezone,
// Future versions might add: maintenanceWindow, powerOutageRisk, alerts, etc.
// Your code doesn't break because you only use what you need
};

// If using TypeScript, mark response as partial to handle future fields
interface SiteDetailsResponse {
siteId: string;
siteName: string;
timezone: string;
siteAddress?: any;
systems?: any[];
[key: string]: any; // Allow additional fields
}

Python Example:

import requests

response = requests.get(
'https://pwrapi.generacy.com/v1/sites/{EXAMPLE_SITE_ID}',
headers={'Authorization': 'Bearer {ACCESS_TOKEN}'}
).json()

# Extract only the fields you need - unknown fields are automatically ignored
site_info = {
'siteId': response.get('siteId'),
'siteName': response.get('siteName'),
'timezone': response.get('timezone'),
'address': response.get('siteAddress', {}),
}

# Future versions might add response['maintenanceWindow'], but it won't affect your code

Avoid This Pattern:

// ❌ Don't do this - it will break if API adds fields
const expectedKeys = ['siteId', 'siteName', 'timezone', 'systems'];
if (Object.keys(response).length > expectedKeys.length) {
throw new Error('Unexpected response format');
}

2. Handle New Enum Values

Implement a catch-all handler for unknown enum values. The API may add new enum values to existing fields at any time.

JavaScript/TypeScript Example:

// GET /v1/sites/{siteId}/alerts returns alert severity: LOW, MEDIUM, HIGH
// Future versions might add: CRITICAL, WARNING, INFO, etc.
const alertSeverity = alert.severity; // "HIGH"

// ✅ DO THIS - Handle unknown values gracefully
function getSeverityLabel(severity) {
switch(severity) {
case 'LOW':
return 'Low Severity';
case 'MEDIUM':
return 'Medium Severity';
case 'HIGH':
return 'High Severity';
default:
// Future versions might add more severity levels
console.warn(`Unknown alert severity: ${severity}`);
return `Alert (${severity})`;
}
}

// ✅ DO THIS - For UI display, use a default fallback
function renderAlertBadge(severity) {
const severityMap = {
'LOW': { color: 'yellow', icon: '⚠️' },
'MEDIUM': { color: 'orange', icon: '⚠️⚠️' },
'HIGH': { color: 'red', icon: '🚨' },
};

const config = severityMap[severity] || {
color: 'gray',
icon: '•'
};

return `<span class="alert-${config.color}">${config.icon} ${severity}</span>`;
}

Python Example:

# GET /v1/sites/{siteId}/alerts returns alert severity: LOW, MEDIUM, HIGH
# Future versions might add: CRITICAL, WARNING, INFO, etc.
alert_severity = alert['severity']

# ✅ DO THIS - Handle unknown values gracefully
def get_severity_label(severity):
severity_map = {
'LOW': 'Low Severity',
'MEDIUM': 'Medium Severity',
'HIGH': 'High Severity',
}
return severity_map.get(severity, f'Alert ({severity})')

# ✅ DO THIS - For filtering/logic, use safe membership tests
def should_escalate_alert(severity):
high_priority = {'HIGH', 'CRITICAL'}
# Unknown severities default to high priority (safer default)
return severity in high_priority or severity not in {'LOW', 'MEDIUM'}

Avoid This Pattern:

// ❌ Don't do this - will break when new enum values are added
if (alert.severity === 'LOW' || alert.severity === 'MEDIUM') {
logMinorAlert();
} else if (alert.severity === 'HIGH') {
alertAdmin();
} else {
throw new Error(`Unknown severity: ${alert.severity}`);
}

// ❌ Don't do this - negative checks for absence
if (alert.severity !== 'HIGH') {
// This logic breaks when new severity levels are added
applyLegacyProcessing();
}

3. Don't Assume Fields Won't Exist

Don't write code that breaks if optional fields are added. Use safe property access.

JavaScript/TypeScript Example:

// GET /v1/sites/{siteId} response
const siteResponse = await getSiteDetails(siteId);

// ✅ DO THIS - Check if field exists before using it
if (siteResponse.deprecationNotice) {
console.warn(`⚠️ API Notice: ${siteResponse.deprecationNotice}`);
}

// ✅ DO THIS - Use optional chaining for nested properties
const systemCount = siteResponse.systems?.length ?? 0;
const timezone = siteResponse.timezone ?? 'America/New_York';

// ✅ DO THIS - Use nullish coalescing to provide sensible defaults
const siteAddress = siteResponse.siteAddress ?? { city: 'Unknown', country: 'Unknown' };

Python Example:

# GET /v1/sites/{siteId} response
site_response = get_site_details(site_id)

# ✅ DO THIS - Check if key exists before using it
if 'deprecationNotice' in site_response:
log_warning(f"API Notice: {site_response['deprecationNotice']}")

# ✅ DO THIS - Use .get() with safe defaults
timezone = site_response.get('timezone', 'America/New_York')
address_city = site_response.get('siteAddress', {}).get('city', 'Unknown')
system_count = len(site_response.get('systems', []))

Avoid This Pattern:

// ❌ Don't do this - assumes fields won't be added
if (!siteResponse.newComplianceField) {
applyLegacyComplianceLogic();
}

// ❌ Don't do this - strict checks for absence
if (siteResponse.maintenanceSchedule === undefined) {
setDefaultMaintenanceSchedule();
// This breaks when API starts returning maintenanceSchedule
}

// ❌ Don't do this - accessing undefined fields
const maintenanceInfo = siteResponse.maintenance.nextScheduled;
// Will crash if maintenance field is added in future