Docs

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.

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');
}

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

PurposeMethodPathResponse
Audit logsGET/cc-ui/v1/audit-logsPaginated
List projectsGET/cc-ui/v1/stacksArray
Project detailsGET/cc-ui/v1/{stackName}Object
List environmentsGET/cc-ui/v1/{stackName}/clustersArray
List releasesGET/cc-ui/v1/{stackName}/releasesPaginated
List resourcesGET/cc-ui/v1/{stackName}/resourceTypesArray
Current userGET/cc-ui/v1/users/currentObject
List usersGET/cc-ui/v1/usersArray
Web componentsGET/cc-ui/v1/web-componentsArray
Create web componentPOST/cc-ui/v1/web-componentsObject

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

StatusMeaningAction
200SuccessProcess response
401UnauthorizedSession expired -- prompt user to refresh the page
403ForbiddenUser lacks permission -- show a clear message
404Not foundResource doesn't exist -- show empty state
500Server errorShow generic error, log details to console
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();
}