Serverless Functions in Query Rules (Spring '22 Release)

Using the Yext Functions framework, developers can now execute arbitrary TypeScript functions within Query Rules in order to power brand new use cases in Answers. By integrating external APIs, we can return real time data in search. Ever-changing data like reservations, flights, account balances, stock prices, product inventory, package tracking updates, or service outages are not static data points that you would typically store in your Knowledge Graph, so it’s crucial that we can fetch them from these external data sources.

Now, with Serverless Functions in Query Rules, a developer could configure an experience where users can ask questions like: “what’s your company’s stock price?" or “Is the blue soccer ball in stock in Soho?” or “where is my package?” or “Is there an outage in my neighborhood?” and expect realtime, accurate answers.

To use functions and custom data in query rules, developers need to:

  • Set up the backend config with one of the new query rule action types: USE_FUNCTION or RETURN_CUSTOM_DATA.
  • Configure the frontend using the Answers API, Core, Headless, or Starter App.

Answers Backend

Depending on whether the developer wants to run the code server-side or client-side, they can use one of two new query rules actions:

  • USE_FUNCTION: This runs a Typescript function from a Yext plugin on our servers and forwards the return value to the front-end.
  • RETURN_CUSTOM_DATA: This simply returns a static JSON blob that is used to signal to the front-end that it’s supposed to trigger an action, like redirecting to a URL or fetching data on the client-side. In addition to triggering actions, custom data could also be used for display. For example, you could write the content for promotions in the back-end as JSON blobs, but then write the display and styling on the front-end.

We’ll use the example of fetching package tracking info, which is something you could do either client-side or server-side.

USE_FUNCTION

The USE_FUNCTION action extends Yext Functions to Answers by allowing developers to activate a TypeScript function and forward the return value to the Answers API. Here’s an example of what one of these functions might look like:

interface QueryPayload {
  input: string,
  context?: Object,
  locale: string,
  experienceKey: string,
  verticalKey?: string,
  referrerPageUrl?: string
  latLong?: LatLong
}

export const fetchTrackingInfo = async (query: QueryPayload) {
  const { input } = payload;
  const trackingRegEx = /\d{5,}/;
  const trackingNumber = input.search(trackingRegEx);
  const trackingURL = 'https://www.mywebsite.com/track-package';
  const response = await axios.get(trackingURL, { ...input })
  return response
}

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 Answers API has about each search. Making these additional inputs available to the function gives the function’s author important context about the search.

To fetch the package tracking info server-side, you can use a query rule with action USE_FUNCTION like this:

{
   "rules": [
      {
         "criteria": {
            "searchTermMatchesRegex": "\\d{5,}"
         },
         "actions": [
            {
               "actionType": "USE_FUNCTION",
               "plugin": "trackingInfoPlugin",
               "function": "fetchTrackingInfo",
               "key": "trackingInfo"
            }
         ]
      }
   ]
}

RETURN_CUSTOM_DATA

The RETURN_CUSTOM_DATA action is somewhat similar to USE_FUNCTION except it skips the function invocation altogether and just returns some arbitrary JSON to the front-end. This means that we don’t have to worry about errors, timeouts, or asynchronous functions, since we aren’t actually executing any code.
Generally speaking, the JSON data that is returned in these rules will be some indicator that the front-end should perform some action, such as redirecting to another page or fetching from an API on the client-side. For example, here’s how we might implement that package tracking example on the client-side:

{
  "rules": [
    {
      "criteria": {
        "searchTermMatchesRegex": "\\d{5,}"
      },
      "actions": [
        {
          "actionType": "RETURN_CUSTOM_DATA",
          "key": "trackingInfo",
          "customData": {
            "action": "FETCH_PACKAGE_DATA"
          }
        }
      ]
    }
  ]
}

All this is doing is basically informing the front-end that it should fetch and display the package data, but it’s up to the front-end to actually implement the logic. Another common use case for RETURN_CUSTOM_DATA will be redirects. In this case, the back-end will simply return a URL to redirect to, and it’s up to the front-end to implement the logic to make the browser redirect.

Answers Frontend

With the two new query rules for the Answers backend, we’ll need the Answers frontend to:
Display data in a card, either returned from an API hit by the Answers backend, or static data returned from the query rule.
Trigger an action, like hitting an API client side (and potentially displaying it), or redirecting the page.

To display the package tracking on the Answers frontend, we use the code here in Answers Headless React.

To access this data in Answers Headless, you would do this:

  const queryRulesData = useAnswersState((s) => s.queryRules.actions);

  const trackingInfo = queryRulesData.find(
    (item) => item.key === "trackingInfo"
  ) as any as TrackingInfo | undefined;

To learn more about using serverless functions in query rules, visit the Functions in Query Rules guide.