API Integration Guide
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: "[email protected]",
// 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();
}Updated about 2 hours ago