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