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"
}
Urlis 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 (API key / anonymous)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.
tisa.context.aadTokenProviderFactoryFor 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
aadTokenProviderFactoryto work, the target resource must be granted API permissions in the SharePoint tenant (API accessin SharePoint Admin Center or viawebApiPermissionRequestsinpackage-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).Eq, Contains, BeginsWith, date ranges, etc.{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: ... }.{ response: ... } return typeResultSetItems and ResultSet mapping configuration