Implicit and Externalized Fetches

When a ServiceDataProvider is configured with properties described in the Service Data Provider Properties section, it will, for the most part, manage fetching data and notifying components implicitly. The exception is the 'fetchChainId'.

Implicit Fetch

A typical configuration for an implicitly fetching ServiceDataProvider would look like this:

"incidentListDataProviderImplicit": {
  "type": "vb/ServiceDataProvider",
  "description": "configuration for implicit fetches",
  "input": "none",
  "defaultValue": {
    "endpoint": "ifixitfast-service/getIncidents",
    "headers": {},
    "keyAttributes": "id",
    "itemsPath": "result",
    "uriParameters": {
      "technician": "{{ $application.user.userId }}"
    }
  }
}

It is important to note that a ServiceDataProvider variable does not cache its data, just its configuration. The data is also not persisted to history, session or localStorage.

Since the data can be arbitrarily large data sets, it is recommended that page authors use other means to cache data on the client, such as the JET offline toolkit cache. This applies to externalized fetches as well.

Externalized Fetch via an Action Chain

When a 'fetchChainId' property is present, the ServiceDataProvider delegates the fetch to the action chain. A typical configuration for a ServiceDataProvider variable (supporting a fetchFirst capability) that externalizes REST will look like the code below. These are the only properties that are allowed to be configured (or that are relevant):

  • capabilities: when this property isn't set, the 'fetchFirst' fetch capability is assumed.

  • fetchChainId

  • idAttribute (deprecated) or keyAttributes

  • itemsPath

  • mergeTransformOptions: this property is defined on the ServiceDataProvider variable, because merging transform options only applies when an action chain (with a REST action) is called in the context of a data provider fetch call.

  • transformsContext: Unlike most transforms-related properties, this property can only be defined on the SDP configuration. Most transforms-related properties can be defined on the REST action (requestTransformationOptions, requestTransformFunctions, responseTransformationFunctions).

  • responseType

"variables": {
  "incidentListTableSource": {
    "type": "vb/ServiceDataProvider",
    "input": "none",
    "persisted": "session",
    "defaultValue": {
      "fetchChainId": "fetchIncidentListChain",
      "keyAttributes": "id",
      "itemsPath": "result",
      "responseType": "application:incidentsResponse"
    }
  }
},
"chains": {
  "fetchIncidentListChain": {
    ...
  },
}

The type definition of "application:incidentsResponse" used by the 'responseType' property can be seen in this example. This structure is similar to the one returned from a REST response. Note that itemsPath is always located within the 'body' property of the response that is returned.

For example, the app-flow.json file for the ServiceDataProvider configuration shown above could look like this:

"incidentsResponse": {
  "type": {
    "status": "string",
    "headers": "object",
    "body": {
      "result": "application:incidentSummary[]"
    }
  }
},
"incidentSummary": {
  "type": {
    "id": "string",
    "problem": "string",
    "priority": "string",
    "status": "string",
    "customer": "application:customer"
  }
},

A sample return value from the action chain would look like this:

{
  "status": "200",
  "headers": {},
  "body": {
    "result": [
      {
        "id": "incident_1",
        "problem": "heater broken",
        "priority": "high",
        "status": "open",
        "customer": {}
      }
    ]
  }
}

Generally, users externalize fetches to ensure full control over how the request and response are processed.

For example, users can connect custom sort and filter query parameters either in the service endpoint or in the REST action. This is the preferred configuration approach. If, however, properties like sortCriteria, filterCriterion, transforms, and so on, are defined on the ServiceDataProvider, they will be ignored, and those configured on the REST action will be used when building the request. It's important to note that sortCriteria / filterCriterion passed in by the component / caller will always get used and (attempted to be) merged with the ones configured on RestAction. See Merge Transform Options Function property.

In the example below, the action chain 'fetchIncidentListChain' defined in the fetchChainId property of the ServiceDataProvider variable above has a typical chain configuration, one of which is a RestAction.

  1. The 'hookHandler' property under configuration chain variable will be automatically generated at design time and is always set to vb/RestHookHandler. SDP implements a custom hookHandler that extends from this class.

  2. If the REST response returns a structure that is exactly what the ServiceDataProvider expects, this can be returned directly (as in the example below). But if the REST response is different from the expected responseType, then an action that maps the REST response to the structure defined by 'responseType' on the SDP needs to be configured.

  3. The last action in the chain will always be a ReturnAction whose payload resembles the REST response whose body resembles 'responseType'. The incidentsResponse response variable in the chain is provided for clarity but is not used by the chain.

  4. If more fields are returned than what the responseType has, SDP will attempt to auto-map the result to the response type.
  5. It's important to not set the 'returnType' property when a ReturnAction is already present in the chain for SDP, because this additionally coerces the response returned to the caller.
"chains": {
   "fetchIncidentListChain": {
    "variables": {
      "configuration": {
        "type": {
          "hookHandler": "vb/RestHookHandler"
        },
        "description": "the configuration for the rest action",
        "input": "fromCaller",
        "required": true
      },
      "response": {
        "type": "application:incidentsResponse"
      }
    },
    "root": "fetchIncidentList",
    "actions": {
      "fetchIncidentList": {
        "module": "vb/action/builtin/restAction",
        "parameters": {
          "endpoint": "ifixitfast-service/getIncidents",
          "uriParams": {
            "technician": "{{ $application.user.userId }}"
          },
          "hookHandler": "{{ $variables.configuration.hookHandler }}",
          "requestTransformOptions": {
            "sort": "{{ $page.variables.sortExpression }}",
            "filter": "{{ $page.variables.filterAtomicExpression }}"
          },
          "requestTransformFunctions": {
            "paginate": "{{ $page.functions.paginate }}",
            "query": "{{ $page.functions.query }}",
            "filter": "{{ $page.functions.filter }}",
            "sort": "{{ $page.functions.sort }}"
          },
          "responseTransformFunctions": {
            "paginate": "{{ $page.functions.paginateResponse }}"
          }
        },
        "outcomes": {
          "success": "returnSuccessResponse",
          "failure": "returnFailureResponse"
        }
      },
      "returnSuccessResponse": {
        "module": "vb/action/builtin/returnAction",
        "parameters": {
          "outcome": "success",
          "payload": "{{ $chain.results.fetchIncidentList }}"
        }
      },
      "returnFailureResponse": {
        "module": "vb/action/builtin/returnAction",
        "parameters": {
          "outcome": "failure",
          "payload": "{{ $chain.results.fetchIncidentList }}"
        }
      }
    }
  }
}