Step 2: USE_FUNCTION Backend
The USE_FUNCTION
action extends
Yext Functions
to Search by allowing developers to activate a TypeScript function and forward the return value to the Search API. We’ll use the example of fetching stock prices.
Write the Function
Follow the Getting Started with Yext Functions guide to create your function. Here’s what the function might look like:
export interface QueryPayload {
input: string, // The query string itself
context?: Object, // Context object passed from the front-end
locale: string, // The locale (language) of the search
experienceKey: string, // The experience key
verticalKey?: string, // Only present on vertical search
referrerPageUrl?: string // The page the user came from
latLong?: LatLong // The user’s location bias, if provided
}
export interface LatLong {
lat: number,
lng: number,
}
export const fetchStockData = async (query: QueryPayload) => {
const { input } = query;
const tickerRegex = /[A-Z]{4}/;
const isTicker = tickerRegex.test(input);
if (input && isTicker) {
const ticker = input.match(tickerRegex)![0];
const date = new Date();
date.setDate(date.getDate() - 1);
// Find the most recent week day before today
const day = date.getDay();
const diff = date.getDate() - day + (day === 0 ? -6 : 1);
date.setDate(diff);
const lastWeekdayString = date.toISOString().split('T')[0];
date.setDate(date.getDate() - 15);
const prevString = date.toISOString().split('T')[0];
const dailyUrl = `https://api.polygon.io/v1/open-close/${ticker}/${lastWeekdayString}?adjusted=true&apiKey=${process.env.POLYGON_API_KEY}`
const graphUrl = `https://api.polygon.io/v2/aggs/ticker/${ticker}/range/1/day/${prevString}/${lastWeekdayString}?apiKey=${process.env.POLYGON_API_KEY}`
const tickerUrl = `https://api.polygon.io/v3/reference/tickers/${ticker}?apiKey=${process.env.POLYGON_API_KEY}`
const dailyRes = await fetch(dailyUrl).then(res => res.json());
const graphRes = await fetch(graphUrl).then(res => res.json());
const tickerRes = await fetch(tickerUrl).then(res => res.json());
return {
graph: graphRes,
daily: dailyRes,
ticker: tickerRes,
}
} else {
return {
status: "No Stock Data Found"
}
}
};
For this example to work, you’ll need a Polygon API key env variable.
You’ll notice that the TS function takes one argument of the type QueryPayload. This is a special interface that contains all of the data that the Search API has about each search, such as the page the user came from or the search query they entered. Making these additional inputs available to the function gives the function’s author important context about the search.
For example, a developer might want to attach user data to the context
object on the front-end and then use that data within their function to look up information specific to the user. Or they might want to change the behavior of their function based on the locale
of the query.
Test the Function
You’ll want to test the function you wrote above to ensure it runs properly. Follow the Local Function Development with Deno guide to learn how.
In our stock price example above, we used the code here to test our function.
Write the Query Rule
Write a query rule in the Search backend JSON editor with the action USE_FUNCTION
. For our stock price example above, if the search term contains a four letter stock symbol, we want to use the function we wrote above. The query rule would look like:
{
"rules": [
{
"criteria": {
"searchTermMatchesRegex": "[A-Z]{4}]"
},
"actions": [
{
"actionType": "USE_FUNCTION",
"plugin": "stockDataPlugin",
"function": "fetchStockData",
"key": "stockData"
}
]
}
]
}
Here’s what each of these inputs does:
plugin
: Plugins are collections of TypeScript files that are defined as CaC resources in a Yext account. Each plugin has a unique ID and can contain many functions.function
: The individual TypeScript function that is triggered by this query rule. In our example, you see thefetchStockData
function is defined above.key
: This is a new property that is required for any action that returns data in the API response. This is necessary because the front-end needs to know which action the data came from so that it can react appropriately.
Timeout
Particularly if the function is making network calls, it will be hard to predict how long its invocation might take. Since we don’t want it slowing down the entire response, developers can add an optional timeout, defined in milliseconds, to the rule, like so:
{
"rules": [
{
"criteria": { ... },
"actions": [
{
"actionType": "USE_FUNCTION",
"plugin": "stockDataPlugin",
"function": "fetchStockData",
"key": "stockData",
"timeout": 250,
}
]
}
]
}
If the function invocation takes any longer than this, the request proceeds without it and adds an indication to the API response that the rule has exceeded the timeout.
Async Functions
When using functions, a developer may not necessarily want to forward the result of the function back to the front-end. For example, maybe a customer wants to programmatically create a Zendesk ticket every time a customer expresses an angry sentiment.
For this, they can make their action asynchronous by adding "async": true
to their rule like so:
{
"rules": [
{
"criteria": { ... },
"actions": [
{
"actionType": "USE_FUNCTION",
"plugin": "stockDataPlugin",
"function": "fetchStockData",
"key": "stockData",
"async": true,
}
]
}
]
}
With asynchronous query rules, nothing is forwarded to the front-end, and the function is not subject to the normal timeout.