TreeINFO-For-M365 documentation
    Preparing search index...

    WS Direct Response – Static Data, Async Calls, SharePoint CAML

    When the custom function returns { response: ... } via ExternalLookupDirectResponse, no internal HTTP request is made — the value is passed directly to the result mapping (ResultSetItems / ResultSet). The function may be async (return a Promise); the result is awaited before processing.

    This pattern covers five common use cases:

    Use case When to use
    Static / hardcoded list Small fixed codebook that does not need a backend
    Custom async calls Multi-step fetch, result merging from multiple endpoints
    External API – API key API secured by API key or publicly accessible (tisa.utils.httpRequest)
    External API – AAD token API protected by Azure Active Directory (aadTokenProviderFactory)
    SharePoint list via PnP SP číselník with CAML query filtering

    The simplest case — the function returns a hardcoded array. No HTTP call is made at all. Useful for small fixed codebooks:

    function ExternalLookupCustomFunction_s_priority(searchText) {
    var all = [
    { id: "1", label: "Low" },
    { id: "2", label: "Medium" },
    { id: "3", label: "High" },
    { id: "4", label: "Critical" }
    ];

    var filtered = searchText
    ? all.filter(function (item) {
    return item.label.toLowerCase().indexOf(searchText.toLowerCase()) !== -1;
    })
    : all;

    return { response: { items: filtered } };
    }

    Configuration (ResultSetItems: "items" matches the wrapper key used in the function):

    {
    "DataSourceType": "WS",
    "DataSourceConfiguration": {
    "Url": "",
    "ResultSetType": "JSON",
    "ResultSet": [
    { "Name": "Id", "Path": "id" },
    { "Name": "Label", "Path": "label" }
    ]
    },
    "IndexValue": { "Expression": "{Id}" },
    "TextValue": { "Expression": "{Label}" },
    "RenderMode": "DropDown"
    }

    Url is required by the configuration schema but is not used when { response: ... } is returned. Set it to an empty string "".


    If the custom function performs its own async calls (e.g. fetches a token, calls an API, merges results from multiple endpoints), it can return the final data directly and skip the internal HTTP call entirely:

    async function ExternalLookupCustomFunction_myLookup(searchText) {
    var token = await fetchToken();
    var res1 = await fetch(
    "https://api.example.com/search?q=" + encodeURIComponent(searchText),
    { headers: { "Authorization": "Bearer " + token } }
    ).then(function (r) { return r.json(); });

    var res2 = await fetch(
    "https://api.example.com/extra/" + res1.id
    ).then(function (r) { return r.json(); });

    return {
    response: {
    items: res1.items.map(function (item) {
    return Object.assign({}, item, res2.byId[item.id]);
    })
    }
    };
    }

    The response field must match the shape expected by ResultSetItems / ResultSet — an object containing an array of items, or a JSON string of such an object.


    tisa.utils.httpRequest is an axios-based HTTP client available in all custom script contexts. Use it for APIs secured by an API key or that are publicly accessible:

    async function ExternalLookupCustomFunction_s_extSearch(searchText) {
    var result = await tisa.utils.httpRequest({
    url: "https://api.example.com/search",
    method: "GET",
    headers: { "X-API-Key": "your-api-key" },
    params: { q: searchText, limit: "20" }
    });

    return { response: result.data };
    }

    result.data contains the parsed response body (JSON object). The shape must match ResultSetItems / ResultSet in the configuration.


    For APIs protected by Azure Active Directory, obtain a token through the SPFx token provider and pass it as a Bearer header. Token acquisition (including caching and renewal) is handled by the SPFx MSAL stack — no manual token management is needed.

    The resource identifier must match the Application ID URI (or client_id) of the target AAD application registration.

    async function ExternalLookupCustomFunction_s_aadSearch(searchText) {
    var tokenProvider = await tisa.context.aadTokenProviderFactory.getTokenProvider();
    var token = await tokenProvider.getToken("https://your-api.azurewebsites.net");

    var result = await tisa.utils.httpRequest({
    url: "https://your-api.azurewebsites.net/api/search",
    method: "GET",
    headers: {
    "Authorization": "Bearer " + token,
    "Accept": "application/json"
    },
    params: { q: searchText }
    });

    return { response: result.data };
    }

    For aadTokenProviderFactory to work, the target resource must be granted API permissions in the SharePoint tenant (API access in SharePoint Admin Center or via webApiPermissionRequests in package-solution.json).


    Using the direct response pattern you can query any SharePoint list with a CAML query via tisa.sp (PnP) and feed the result directly into the ExternalLookup result mapping — no separate REST endpoint needed.

    This approach is useful for codebooks / číselníky stored in SharePoint that require filtered queries (e.g. full-text contains, active-only items, ordered results) beyond what the built-in SPDataSource supports.

    {
    "DataSourceType": "WS",
    "DataSourceConfiguration": {
    "Url": "",
    "ResultSetType": "JSON",
    "ResultSet": [
    { "Name": "Id", "Path": "ID" },
    { "Name": "Nazev", "Path": "Title" },
    { "Name": "Kod", "Path": "s_kod" }
    ]
    },
    "IndexValue": { "Expression": "{Id}" },
    "TextValue": { "Expression": "{Nazev}" },
    "RenderMode": "DataTable",
    "DataTableElements": [
    { "ColumnTitle": "Kód", "Expression": "{Kod}" },
    { "ColumnTitle": "Název", "Expression": "{Nazev}" }
    ]
    }

    The function uses tisa.sp.web (PnP) to execute a CAML query against a SharePoint list. The results are wrapped in { value: [...] } to match ResultSetItems: "value".

    Use tisa.page.serverRelativeUrl to get the server-relative URL of the current web dynamically — no hardcoded site path needed.

    async function ExternalLookupCustomFunction_s_ciselnik(searchText) {
    var list = tisa.sp.web.getList(tisa.page.serverRelativeUrl + "/Lists/Ciselnik");

    var caml = {
    ViewXml:
    '<View><Query>' +
    '<Where>' +
    '<And>' +
    '<Eq><FieldRef Name="s_aktivni"/><Value Type="Boolean">1</Value></Eq>' +
    '<Contains><FieldRef Name="Title"/><Value Type="Text">' + searchText + '</Value></Contains>' +
    '</And>' +
    '</Where>' +
    '<OrderBy><FieldRef Name="Title" Ascending="TRUE"/></OrderBy>' +
    '</Query>' +
    '<ViewFields>' +
    '<FieldRef Name="ID"/>' +
    '<FieldRef Name="Title"/>' +
    '<FieldRef Name="s_kod"/>' +
    '</ViewFields>' +
    '<RowLimit>50</RowLimit>' +
    '</View>'
    };

    var items = await list.getItemsByCAMLQuery(caml);

    return {
    response: { value: items }
    };
    }
    • ResultSetItems: "value" matches the { value: [...] } wrapper returned by the function.
    • ResultSet[].Path maps directly to SP item property names (e.g. ID, Title, s_kod).
    • The CAML query can include any SP filter logic: Eq, Contains, BeginsWith, date ranges, etc.
    • For lists on a different site, replace {SiteUrl} with an absolute URL or another expression token (e.g. {WebAppUrl}/sites/other).
    • Url: "" — required by the configuration schema but never called; set to an empty string when using { response: ... }.