Skip to main content

Component-to-Component Communication

In a Blocklet application composed of multiple components, enabling them to communicate with each other securely and reliably is essential. The Blocklet SDK provides a high-level utility, component.call, designed specifically for this purpose. This method simplifies inter-component API calls by handling service discovery, request signing, and automatic retries.

This approach is more robust than making direct HTTP requests because it abstracts away the complexities of the underlying environment, such as dynamic ports and Docker networking, while ensuring that all communication is authenticated.

Making a Secure API Call

The primary method for inter-component communication is component.call. It acts as a wrapper around an HTTP client (axios) but automatically injects the necessary authentication headers to verify the calling component's identity.

Basic Usage

Here is a basic example of one component calling an API endpoint on another component named 'user-service'.

Calling another component

javascript
import component from '@blocklet/sdk/component';

async function getUserProfile(userId) {
  try {
    const response = await component.call({
      name: 'user-service', // Name, DID, or title of the target component
      method: 'GET',
      path: `/api/users/${userId}`,
    });

    console.log('User Profile:', response.data);
    return response.data;
  } catch (error) {
    console.error('Failed to call user-service:', error.message);
  }
}

How It Works

The component.call function streamlines the communication process through several key steps:

  1. Service Discovery

    It looks up the target component (e.g., 'user-service') in the application's component registry to find its current location and metadata.

  2. Endpoint Resolution

    It constructs the correct internal URL to reach the component, automatically handling complexities like Docker container networking.

  3. Request Signing

    Before sending the request, it automatically adds special x-component-* headers. These headers contain a signature generated using the calling component's secret key, proving its identity.

  4. API Call

    It executes the HTTP request using the configured method, path, and data.

  5. Automatic Retries

    If the request fails due to a transient server error (e.g., a 5xx status code), it will automatically retry the request a few times with an increasing delay (exponential backoff).

This flow ensures that the communication is both reliable and secure. The receiving component can use the session middleware to verify the signature and authorize the request.

Component-to-Component Communication

call Parameters

The component.call function accepts an options object with the following properties:

  • name string (required) — The name, title, or DID of the target component to call.
  • method string (default: POST) — The HTTP method for the request (e.g., 'GET', 'POST', 'PUT', 'DELETE').
  • path string (required) — The API path on the target component (e.g., '/api/v1/resource').
  • data any — The request body, typically used with POST, PUT, or PATCH methods.
  • params any — URL query parameters to be appended to the request URL.
  • headers object — An object of custom headers to be sent with the request.
  • timeout number — Request timeout in milliseconds.
  • responseType string — The type of data that the server will respond with. For example, 'stream'.

Return Value

The function returns a Promise that resolves to an AxiosResponse object, which contains properties like data, status, and headers.

Advanced Usage

Customizing Retry Behavior

You can customize the automatic retry logic by passing a second argument to component.call. This is useful for adjusting to the specific reliability needs of an endpoint.

Custom Retry Options

javascript
import component from '@blocklet/sdk/component';

const callOptions = {
  name: 'data-processor',
  method: 'POST',
  path: '/api/process',
  data: { job: 'some-long-job' },
};

const retryOptions = {
  retries: 5,       // Attempt 5 times in total
  minTimeout: 1000, // Wait at least 1 second between retries
  factor: 2,        // Double the wait time after each failed attempt
};

async function processData() {
  const response = await component.call(callOptions, retryOptions);
  return response.data;
}

The retryOptions object can have the following properties:

  • retries number (default: 3) — The total number of attempts to make.
  • factor number (default: 2) — The exponential factor to use for backoff.
  • minTimeout number (default: 500) — The minimum timeout between retries in milliseconds.
  • maxTimeout number (default: 5000) — The maximum timeout between retries in milliseconds.
  • randomize boolean (default: true) — Whether to randomize the timeout.
  • onFailedAttempt function — A callback function that is invoked on each failed attempt.

Handling Stream Responses

For endpoints that return a stream (e.g., downloading a large file), you can set responseType: 'stream'. This allows you to process the data as it arrives without buffering the entire response in memory.

Streaming a File

javascript
import fs from 'fs';
import component from '@blocklet/sdk/component';

async function downloadBackup() {
  const response = await component.call({
    name: 'backup-service',
    method: 'GET',
    path: '/api/export',
    responseType: 'stream',
  });

  const writer = fs.createWriteStream('backup.zip');
  response.data.pipe(writer);

  return new Promise((resolve, reject) => {
    writer.on('finish', resolve);
    writer.on('error', reject);
  });
}

By using component.call, you can build robust and secure multi-component applications with ease. The next logical step is to learn how to protect your component's API endpoints by verifying these incoming calls. See the Session Middleware guide for details.