Prerequisites
This tutorial uses the Client List demo app included with the io.Connect Desktop seed project’s demos component. Make sure this component is installed before starting. The demos consist of three web applications: Client Contact, Client List, Client Portfolio.
Tested and verified with io.Connect Desktop 10.2
Search in io.Connect Desktop
io.Connect Desktop offers a flexible Search API that can let users search across apps and systems. Search clients send queries; providers search their data and return matching results. It supports custom result types, query debouncing, result-limit settings, and actions associated with results, such as displaying an instrument’s chart. In this guide, we’ll use the Client List demo to make individual client records searchable from Launchpad.
When you open Launchpad, its search bar already lets you find registered apps. For example, if the Client List demo app is enabled, searching for Client finds that app + workspaces, etc that contain the search term. This comes from Desktop’s built-in search provider, which also supports Workspaces, Layouts, and actions.
But what if you search for a person listed inside that same app, such as Nola Rios? The built-in provider doesn’t search the app’s records, so it won’t return that person unless a provider has been added for those records.
We’ll add that functionality with a small custom search provider. Launchpad will send it the search text, our JavaScript will find matching clients in the demo data, and the provider will return their names and emails for Launchpad to display.
1. Check the existing data
Open Clients → Client List and check that people are displayed.
The same records are available at localhost:22060/clients. One record has this shape:
{
"_id": "017892d7-e11e-4fde-90db-406bfc65f29e",
"displayName": "Vernon Mullen",
"emails": ["vernon.d.mullen@gmail.com"]
}
2. Add the provider
This tutorial assumes you already know how to add a web app and register its app definition in Desktop - io.Connect Desktop Documentation - How to... > Interop-Enable Your Apps > JavaScript Use your usual project structure and build process.
The snippets below explain the Search integration in order. They belong in the same provider JavaScript app, with initialization and data loading inside your existing async startup function.
Initialize the Search API
import IODesktop from "@interopio/desktop";
import IOSearch from "@interopio/search-api";
// Inside your app's async startup function:
const io = await IODesktop({ libraries: [IOSearch] });
Once initialization completes, io.search is available.
Get the records to search
Load the same records as Client List:
const response = await fetch("http://localhost:22060/clients");
if (!response.ok) throw new Error(`Client service: HTTP ${response.status}`);
const clients = await response.json();
In this case we load from a file for simplicity.
Register a provider and its result type
const type = { name: "simple.clients", displayName: "Client List People" };
const provider = await io.search.registerProvider({
name: "simple-client-search",
types: [type]
});
This makes the provider discoverable by Launchpad. The provider’s name identifies it; the type identifies the kind of records it returns.
Handle a query and return results
provider.onQuery(query => {
try {
const text = query.search.trim().toLowerCase();
if (!text) return;
const matches = clients.filter(client =>
[client.displayName, client._id, ...(client.emails || [])]
.some(value => value?.toLowerCase().includes(text))
);
matches.forEach(client => query.sendResult({
id: client._id,
type,
displayName: client.displayName,
description: (client.emails || []).join(", ")
}));
} catch (error) {
query.error(error.message);
} finally {
query.done();
}
});
onQuery() runs when the provider receives a search. query.search contains what the user typed. The filter() is your search logic: here it matches names, IDs and emails without case sensitivity.
sendResult() sends one matching record to Launchpad. displayName is the main label, description is the email underneath, and type puts it in the correct group.
Load the built provider script from your app’s HTML (Remember to include the Search API library in your app’s bundle):
<script src="provider.bundle.js"></script>
3. Tell Desktop to start it
Register the following app definition, replacing <URL of provider.html> with your provider page’s URL:
[
{
"name": "simple-client-search",
"title": "Client Search Provider",
"type": "window",
"service": true,
"hidden": true,
"autoStart": true,
"allowMultiple": false,
"ignoreFromLayouts": true,
"details": {
"url": "<URL of provider.html>",
"hidden": true,
"backgroundThrottling": false
}
}
]
autoStart starts the provider with Desktop; hidden keeps its window out of the way.
4. Restart and test
Enter Nola in Launchpad. You should see Nola Rios under Client List People, with his email below the name.
This will only displays records, since clicking a row has no configured action.
5. Optional: open the selected client onclick
We can easily add an action next, e.g., open the demo’s existing Client Contact interface for the search result.
Enable the Apps API in your initialization:
const io = await IODesktop({ libraries: [IOSearch], apps: true, appManager: false });
After loading clients, register an Interop method before registering the search provider:
await io.interop.register("ClientSearch.OpenClient", async ({ id }) => {
if (!clients.some(client => client._id === id)) throw new Error("Unknown client");
const instance = await io.apps.instances.start({
name: "simple-client-contact", context: { clientId: id }
});
return { instanceId: instance.id };
});
Add this property to the result object passed to query.sendResult():
action: {
method: "ClientSearch.OpenClient",
params: { id: client._id },
target: { instance: io.interop.instance.instance }
}
Selecting a result in Launchpad invokes the method with that record’s ID. The target directs it to the provider instance that created the result.
Register this additional app definition:
{
"name": "simple-client-contact",
"title": "Client Contact — Search",
"type": "window",
"hidden": true,
"allowMultiple": true,
"ignoreFromLayouts": true,
"customProperties": { "useChannels": false },
"details": {
"url": "http://localhost:22080/clpc/dist/#/clientcontact/",
"width": 900,
"height": 760
}
}
Now when you click Nola Rios in the search results his Client details will open.
Reference: Search API documentation




