customconditionaldatafetch
A guarded variant of customgenericdatafetch that skips duplicate calls, chains dependent fetches, and conditionally fires only when a fetchCondition is met.
A guarded variant of customgenericdatafetch that skips duplicate calls, chains dependent fetches, and conditionally fires only when a fetchCondition is met.
Read Form Rules before using this component — keys, labels, required fields, expressions, visibility, and validation all follow shared conventions.
When to use
- Preventing duplicate API calls when a field changes after the first call already executed
- Chaining dependent fetches so each fires only after the previous one succeeds (check → delete → create patterns)
- Skipping a fetch entirely when a
fetchConditionis not met (e.g. only fetch passport validity when document type is"PASSPORT") - Hidden background fetches that need conditional or sequential execution
When NOT to use
Instead of customconditionaldatafetch, use… | For… |
|---|---|
customgenericdatafetch | Any single fetch that does not need conditional skipping or sequential chaining |
customgenericguardeddatafetch | A visible input that triggers a main fetch only after pre-flight checks all pass |
genericbuttonfetch | A standalone button trigger with no input field |
customotpdatafetch | OTP-verified data fetch (send OTP, verify, then fetch) |
customconditionaldatafetch is the "guarded" sibling of customgenericdatafetch. If you have neither a fetchCondition nor a chained ON_SUCCESS dependency, use customgenericdatafetch instead — there is no reason to use this variant without those features.
Props
| Prop | Type | Required? | Description |
|---|---|---|---|
label | string | required | Internal identifier — typically set to the field key; never shown to the user. |
url | string | required | Integration endpoint path — always "/integration/v1/fetch/sync". |
endpointCode | string | required | Integration endpoint code for this fetch. |
triggerType | string | required | "FIELD_LISTENER" to fire on field change; "TRIGGER_BUTTON" on button click; "ON_SUCCESS" to chain after another fetch. |
useBaseUrl | boolean | optional | Prepend the API gateway base URL; set true for all production endpoints. |
debounceTime | number | optional | Milliseconds to wait after trigger before firing (default 500). |
hideField | boolean | optional | Always set to true; this component has no visible UI. |
hideFromPreview | boolean | optional | Set true to exclude this field from the application preview. |
required | boolean | optional | Always false. |
defaultRequired | boolean | optional | Always false. |
populates | array | optional | { valueKey, targetKey } mappings to write response values into the form model. |
payloadKey | string | optional | Single-key payload shorthand — the value of this model field is sent under this key. |
payloadKeyMappings | array | optional | Multi-field payload — see shape below. |
useMultiplePayloadKeys | boolean | optional | Set true when payloadKeyMappings has more than one entry. |
populateRelativeToRootForm | boolean | optional | Populate fields across sections using root-form paths. |
onSuccessOf | string | conditional | Key of the fetch that must succeed before this one fires — required when triggerType: "ON_SUCCESS". |
fetchCondition | object | optional | Skip this fetch unless the condition passes — see shape below. |
errorMapping | object | optional | Advanced API error parsing — see shape below. |
showGlobalError | boolean | optional | Surface errors in the global form error display. |
payloadKeyMappings item shape
| Key | Type | Description |
|---|---|---|
key | string | Payload field name sent to the endpoint |
isStatic | boolean | true to send a hardcoded value |
staticValue | string | array | Value sent when isStatic: true |
sourceField | string | Form model key whose current value is sent (used when not static) |
fetchCondition shape
| Key | Type | Description |
|---|---|---|
field | string | Form model key to evaluate. |
operator | string | "EQ" equals, "NEQ" not equals, "GT" greater than, "LT" less than, "GTE" ≥, "LTE" ≤, "CONTAINS" text or array contains. |
value | any | Value to compare the field against. |
searchInRootForm | boolean | true to resolve field from the root form model rather than local scope. |
errorMapping shape
| Key | Type | Description |
|---|---|---|
detectionType | string | "advanced" for structured error parsing. |
useApiMessage | boolean | Extract the error text from the API response body. |
defaultErrorCode | string | Validator key to emit when the API returns an error (e.g. "invalidGenericInput"). |
errorMessagePath | string | Dot-path in the response body to the error message (e.g. "responseMessage.message"). |
Examples
Conditional skip — only fetch when a condition is met
Only fetches passport validity when the selected document type is "PASSPORT".
{
"key": "CHECK_PASSPORT_VALIDITY",
"type": "customconditionaldatafetch",
"props": {
"label": "CHECK_PASSPORT_VALIDITY",
"url": "/integration/v1/fetch/sync",
"useBaseUrl": true,
"endpointCode": "PASSPORT_CHECK",
"triggerType": "FIELD_LISTENER",
"debounceTime": 500,
"hideField": true,
"required": false,
"defaultRequired": false,
"payloadKey": "passportNumber",
"fetchCondition": {
"field": "DOCUMENT_TYPE",
"operator": "EQ",
"value": "PASSPORT"
},
"populates": [{ "valueKey": "isValid", "targetKey": "PASSPORT_VALID" }]
},
"validation": {
"messages": {
"invalidInput": "Could not validate passport",
"invalidGenericInput": "A technical issue occurred. Please try again later."
}
}
}Sequential chain — check, conditionally delete, then create
The entry-point fetch uses FIELD_LISTENER; all chained fetches use ON_SUCCESS and reference the predecessor via onSuccessOf. Each branch uses a fetchCondition to decide whether to fire.
[
{
"key": "CHECK_DRAFT",
"type": "customconditionaldatafetch",
"props": {
"label": "CHECK_DRAFT",
"url": "/integration/v1/fetch/sync",
"useBaseUrl": true,
"endpointCode": "RDB_BUSINESS_FETCH_1",
"triggerType": "FIELD_LISTENER",
"debounceTime": 500,
"hideField": true,
"required": false,
"defaultRequired": false,
"payloadKeyMappings": [
{ "key": "surname", "sourceField": "APPLICANT_SURNAME" },
{ "key": "personDocNo", "sourceField": "APPLICANT_DOCUMENT_NO" },
{ "key": "email", "sourceField": "APPLICANT_EMAIL" }
],
"useMultiplePayloadKeys": true,
"populates": [
{ "valueKey": "can_initiate_status", "targetKey": "CAN_NOW_INITIATE" },
{ "valueKey": "draft_applications", "targetKey": "FETCHED_DRAFT_APPLICATIONS" }
]
},
"validation": {
"messages": {
"invalidInput": "Error retrieving draft status",
"invalidGenericInput": "A technical issue occurred. Please try again later."
}
}
},
{
"key": "DELETE_DRAFT",
"type": "customconditionaldatafetch",
"props": {
"label": "DELETE_DRAFT",
"url": "/integration/v1/fetch/sync",
"useBaseUrl": true,
"endpointCode": "RDB_DELETE_DRAFT_BUSINESS",
"triggerType": "ON_SUCCESS",
"onSuccessOf": "CHECK_DRAFT",
"debounceTime": 500,
"hideField": true,
"hideFromPreview": true,
"required": false,
"defaultRequired": false,
"fetchCondition": {
"field": "CAN_NOW_INITIATE",
"operator": "NEQ",
"value": "success",
"searchInRootForm": false
},
"payloadKeyMappings": [
{ "key": "applications", "sourceField": "FETCHED_DRAFT_APPLICATIONS" }
],
"useMultiplePayloadKeys": true
},
"validation": {
"messages": {
"invalidInput": "Error deleting draft"
}
}
},
{
"key": "CREATE_DRAFT_DIRECT",
"type": "customconditionaldatafetch",
"props": {
"label": "CREATE_DRAFT_DIRECT",
"url": "/integration/v1/fetch/sync",
"useBaseUrl": true,
"endpointCode": "RDB_INITIATE_DBR_PUSH_1",
"triggerType": "ON_SUCCESS",
"onSuccessOf": "CHECK_DRAFT",
"debounceTime": 500,
"hideField": true,
"hideFromPreview": true,
"required": false,
"defaultRequired": false,
"fetchCondition": {
"field": "CAN_NOW_INITIATE",
"operator": "EQ",
"value": "success",
"searchInRootForm": false
},
"payloadKeyMappings": [
{ "key": "surname", "sourceField": "APPLICANT_SURNAME" },
{ "key": "email", "sourceField": "APPLICANT_EMAIL" }
],
"useMultiplePayloadKeys": true,
"showGlobalError": true,
"errorMapping": {
"detectionType": "advanced",
"useApiMessage": true,
"defaultErrorCode": "invalidGenericInput",
"errorMessagePath": "responseMessage.message"
},
"populates": [{ "valueKey": "data.id", "targetKey": "BUSINESS_ID" }]
},
"validation": {
"messages": {
"invalidInput": "Error creating application",
"invalidGenericInput": "A technical issue occurred. Please try again later."
}
}
},
{
"key": "CREATE_DRAFT_AFTER_DELETE",
"type": "customconditionaldatafetch",
"props": {
"label": "CREATE_DRAFT_AFTER_DELETE",
"url": "/integration/v1/fetch/sync",
"useBaseUrl": true,
"endpointCode": "RDB_INITIATE_DBR_PUSH_1",
"triggerType": "ON_SUCCESS",
"onSuccessOf": "DELETE_DRAFT",
"debounceTime": 500,
"hideField": true,
"hideFromPreview": true,
"required": false,
"defaultRequired": false,
"payloadKeyMappings": [
{ "key": "surname", "sourceField": "APPLICANT_SURNAME" },
{ "key": "email", "sourceField": "APPLICANT_EMAIL" }
],
"useMultiplePayloadKeys": true,
"showGlobalError": true,
"errorMapping": {
"detectionType": "advanced",
"useApiMessage": true,
"defaultErrorCode": "invalidGenericInput",
"errorMessagePath": "responseMessage.message"
},
"populates": [{ "valueKey": "data.id", "targetKey": "BUSINESS_ID" }]
},
"validation": {
"messages": {
"invalidInput": "Error creating application",
"invalidGenericInput": "A technical issue occurred. Please try again later."
}
}
}
]Common mistakes
- Omitting
url— always set to/integration/v1/fetch/sync; the component throws at runtime if missing. - Omitting
endpointCode— throws at runtime; always required. - Using
triggerType: "FIELD_LISTENER"on chained fetches — causes race conditions; only the entry-point fetch should beFIELD_LISTENER; all dependent fetches must useON_SUCCESS. - Using
triggerType: "ON_SUCCESS"without settingonSuccessOf— the chain has no anchor and never fires. - Omitting
useMultiplePayloadKeys: truewhenpayloadKeyMappingscontains more than one entry — only the first key is sent to the API. - Forgetting hidden status fields that
fetchConditionreads — the condition field must exist in the model (typically a hiddeninputfield populated by an earlierpopulatesmapping). - Creating circular chains (
Awaits forB,Bwaits forA) — infinite wait, fetch never fires. - Using
customconditionaldatafetchwith nofetchConditionand no chaining — usecustomgenericdatafetchinstead.
Checklist
-
keyisUPPER_SNAKE_CASEand unique across the entire form -
props.labelmatcheskey(this is a hidden internal field) -
urlis"/integration/v1/fetch/sync"anduseBaseUrl: true -
endpointCodeis set -
triggerTypeis one of"FIELD_LISTENER","TRIGGER_BUTTON", or"ON_SUCCESS" -
onSuccessOfis set whenevertriggerType: "ON_SUCCESS" -
hideField: true(this component has no visible UI) -
useMultiplePayloadKeys: truewheneverpayloadKeyMappingshas more than one entry - Every field referenced in
fetchCondition.fieldexists in the model - Chained fetches use
ON_SUCCESS, never a secondFIELD_LISTENER - At least one of
fetchConditionor chaining is in use (otherwise prefercustomgenericdatafetch) -
validation.messagesincludesinvalidInputandinvalidGenericInput
customcascadingdropdowns
A multi-level cascading dropdown for hierarchical selection. Each level filters the options available in the next level based on the parent selection.
customcrvsidinput
An identity input specialised for CRVS (Civil Registration and Vital Statistics) lookups, validating against the configured CRVS endpoint.