API Integration Guide
Integrate web components with the Facets API for live data.
This guide explains how Facets web components authenticate, make API calls, access user context, and navigate within the platform.
Authentication Model: Session Cookie Inheritance
Web components embedded in Facets automatically inherit the active session cookie. This means:
- You do NOT need API keys, tokens, or explicit authentication headers
- The browser sends the session cookie with every
fetch()call automatically - You MUST use relative URLs (not absolute) for this to work
// Correct -- relative URL, session cookie sent automatically
const response = await fetch('/cc-ui/v1/audit-logs?size=10');
// WRONG -- absolute URL bypasses cookie inheritance
const response = await fetch('https://mycompany.console.facets.cloud/cc-ui/v1/audit-logs');
// WRONG -- explicit auth header (not needed and may conflict)
const response = await fetch('/cc-ui/v1/audit-logs', {
headers: { 'Authorization': 'Bearer ...' }
});Making API Calls
Basic GET Request
async fetchData() {
try {
this.setLoading(true);
this.clearError();
const response = await fetch('/cc-ui/v1/your-endpoint');
if (!response.ok) {
throw new Error(`API request failed with status ${response.status}`);
}
const data = await response.json();
this.data = data.content || data; // Paginated endpoints use .content
this.updateUI();
} catch (error) {
this.showError(`Failed to load data: ${error.message}`);
console.error('API error:', error);
} finally {
this.setLoading(false);
}
}GET with Query Parameters
async fetchWithParams() {
const params = new URLSearchParams({
number: this.currentPage.toString(),
size: this.pageSize.toString(),
});
// Add optional filters
if (this.filters.search) {
params.append('search', this.filters.search);
}
const response = await fetch(`/cc-ui/v1/your-endpoint?${params}`);
const data = await response.json();
// Paginated responses include:
// data.content - array of items
// data.totalPages - total number of pages
// data.number - current page (0-indexed)
// data.totalElements - total item count
}POST Request
async createItem(payload) {
const response = await fetch('/cc-ui/v1/your-endpoint', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload),
});
if (!response.ok) {
const error = await response.text();
throw new Error(`Create failed: ${error}`);
}
return response.json();
}PUT Request
async updateItem(id, payload) {
const response = await fetch(`/cc-ui/v1/your-endpoint/${id}`, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload),
});
if (!response.ok) {
throw new Error(`Update failed: ${response.status}`);
}
return response.json();
}DELETE Request
async deleteItem(id) {
const response = await fetch(`/cc-ui/v1/your-endpoint/${id}`, {
method: 'DELETE',
});
if (!response.ok) {
throw new Error(`Delete failed: ${response.status}`);
}
}User Context
Facets passes user information to your component as a JSON-encoded user attribute. Parse it in connectedCallback():
connectedCallback() {
// Parse user data from attribute
const userAttr = this.getAttribute('user');
if (userAttr) {
try {
this.user = JSON.parse(userAttr);
// this.user may contain:
// {
// email: "user@company.com",
// name: "John Doe",
// roles: [...],
// ...
// }
} catch (e) {
console.warn('Failed to parse user attribute:', e);
}
}
this.setupEventListeners();
this.fetchData();
}Using User Data
// Display current user
renderHeader() {
const greeting = this.user ? `Hello, ${this.user.name}` : 'Hello';
this.shadowRoot.getElementById('greeting').textContent = greeting;
}
// Filter data by current user
fetchMyItems() {
const email = this.user?.email || '';
return fetch(`/cc-ui/v1/items?createdBy=${encodeURIComponent(email)}`);
}Contextual Attributes
When registering a web component, you can define contextualAttributes -- a key-value map that Facets passes to your component as HTML attributes:
{
"contextualAttributes": {
"theme": "dark",
"maxItems": "50",
"defaultProject": "production"
}
}Read them in your component:
connectedCallback() {
this.theme = this.getAttribute('theme') || 'light';
this.maxItems = parseInt(this.getAttribute('maxItems') || '10', 10);
this.defaultProject = this.getAttribute('defaultProject');
}Navigation Bridge
To navigate within the Facets UI from your web component, dispatch a facets-navigate custom event:
navigateTo(route, queryParams = {}) {
this.dispatchEvent(new CustomEvent('facets-navigate', {
bubbles: true,
composed: true, // Required to cross Shadow DOM boundary
detail: { route, queryParams }
}));
}
// Examples:
this.navigateTo('/projects/my-project');
this.navigateTo('/projects/my-project/environments', { tab: 'clusters' });The composed: true flag is essential -- it allows the event to bubble out of the Shadow DOM and reach the Facets navigation handler.
Common API Endpoints
| Purpose | Method | Path | Response |
|---|---|---|---|
| Audit logs | GET | /cc-ui/v1/audit-logs | Paginated |
| List projects | GET | /cc-ui/v1/stacks | Array |
| Project details | GET | /cc-ui/v1/{stackName} | Object |
| List environments | GET | /cc-ui/v1/{stackName}/clusters | Array |
| List releases | GET | /cc-ui/v1/{stackName}/releases | Paginated |
| List resources | GET | /cc-ui/v1/{stackName}/resourceTypes | Array |
| Current user | GET | /cc-ui/v1/users/current | Object |
| List users | GET | /cc-ui/v1/users | Array |
| Web components | GET | /cc-ui/v1/web-components | Array |
| Create web component | POST | /cc-ui/v1/web-components | Object |
For the full API reference, use the Facets Swagger documentation at https://<your-instance>.console.facets.cloud/swagger-ui/index.html.
Error Handling Patterns
HTTP Status Codes
| Status | Meaning | Action |
|---|---|---|
| 200 | Success | Process response |
| 401 | Unauthorized | Session expired -- prompt user to refresh the page |
| 403 | Forbidden | User lacks permission -- show a clear message |
| 404 | Not found | Resource doesn't exist -- show empty state |
| 500 | Server error | Show generic error, log details to console |
Recommended Pattern
async safeFetch(url, options = {}) {
const response = await fetch(url, options);
if (response.status === 401) {
this.showError('Session expired. Please refresh the page.');
return null;
}
if (response.status === 403) {
this.showError('You do not have permission to access this resource.');
return null;
}
if (!response.ok) {
const text = await response.text().catch(() => '');
throw new Error(`Request failed (${response.status}): ${text || 'Unknown error'}`);
}
return response.json();
}