preFetchChecks)The Application Property Uniqueness Check is a pre-fetch validation pipeline available on customgenericguardeddatafetch formly fields. It validates whether a given application property value (e.g., NID, UPI) already has an active application in the system before the main data fetch runs. This prevents duplicate applications from being submitted.
The pipeline executes in order: NID Verification → Pre-Fetch Checks → Main Data Fetch. Each step is optional and independently configurable. The pipeline stops on the first failure.
User clicks "Fetch" button
│
▼
┌─────────────────────────┐
│ NID Verification │ (optional - if nidVerification.enabled)
│ Validates identity via │
│ NIDA lookup │
└──────────┬──────────────┘
│ pass
▼
┌─────────────────────────┐
│ Pre-Fetch Checks[] │ (optional - if preFetchChecks[].enabled)
│ Runs each check in │
│ sequence, stops on │
│ first failure │
└──────────┬──────────────┘
│ all pass
▼
┌─────────────────────────┐
│ Main Data Fetch │ (standard fetch logic)
│ Fetches and populates │
│ form fields │
└─────────────────────────┘
| Item | Value |
|---|---|
| Field type | customgenericguardeddatafetch |
preFetchChecks ArrayEach element in the preFetchChecks array defines one validation check. Multiple checks run sequentially; the pipeline halts on the first failure.
| Property | Type | Required | Default | Description |
|---|---|---|---|---|
enabled |
boolean |
Yes | — | Enables or disables this check. Set to false to skip without removing config. |
url |
string |
Yes | — | API endpoint path (e.g., /integration/v1/fetch/sync). |
endpointCode |
string |
Yes | — | The endpoint code registered in the API gateway. For uniqueness checks: IREMBOHUB_VALIDATE_APPLICATION_PROPERTY_UNIQUENESS_FETCH. |
useBaseUrl |
boolean |
No | false |
If true, prepends the API gateway base URL to url. |
method |
'GET' \| 'POST' |
No | POST |
HTTP method for the request. |
payloadKeyMappings |
PayloadKeyMapping[] |
No | [] |
Maps form field values or static values into the request payload. |
errorKey |
string |
No | — | Error key set on the form control. Must match a key in validation.messages for Formly to display the message. |
errorMessage |
string |
No | — | Static fallback error message if no translation is found. |
blockOnSuccess |
BlockOnSuccess |
No | — | Defines conditions under which a successful API response should still block the user (e.g., unique: false means a duplicate exists). |
payloadKeyMappings ArrayEach mapping defines one key in the request payload.
| Property | Type | Required | Description |
|---|---|---|---|
key |
string |
Yes | The key name in the outgoing payload. |
isStatic |
boolean |
Yes | If true, uses staticValue. If false, reads from the form field specified in sourceField. |
staticValue |
any |
Conditional | The static value to send. Required when isStatic: true. Can be a string, array, object, etc. |
sourceField |
string |
Conditional | The form field key whose current value will be used. Required when isStatic: false. |
Standard payload keys for the uniqueness check endpoint:
| Key | Description | Example |
|---|---|---|
serviceId |
UUID of the service to check against | "f17f077b-9997-4239-864d-35921fb237b0" |
propertyKey |
The application property key to check | "identificationDocumentNumber", "Plotupi69Fu3" |
propertyValue |
The value to check for uniqueness (usually dynamic from form) | Sourced from the form field |
applicationStates |
Array of application statuses to match against | ["PENDING", "IN_PROGRESS", "APPROVED"] |
blockOnSuccess ObjectDefines when a successful API response should still block the user. The API returns a response indicating whether the property value is unique. If it is not unique, the pipeline blocks.
| Property | Type | Required | Description |
|---|---|---|---|
responseField |
string |
Yes | Dot-notation path to the field in the API response to evaluate (e.g., data.unique). |
value |
any |
Yes | The value at responseField that triggers blocking. Typically false (meaning "not unique"). |
messagePath |
string |
No | Dot-notation path to a raw error message in the response body. |
messageTranslationKey |
string |
No | An ngx-translate key for a translated, dynamic error message. Preferred approach. |
messageTranslationParams |
Record<string, string> |
No | Maps translation parameter names to dot-notation paths in the response. Used to inject dynamic values (e.g., application number) into the translated message. |
Message resolution priority: messageTranslationKey → messagePath → errorMessage (static fallback).
nidVerification Object (Optional)If the fetch field also requires NID verification before the uniqueness check:
| Property | Type | Required | Description |
|---|---|---|---|
enabled |
boolean |
Yes | Enable/disable NID verification step. |
idType |
'NID' \| 'CHILD_ID' \| 'NIN' |
Yes | The type of identity document. |
endpointCode |
string |
Yes | Endpoint code for NIDA lookup (e.g., NIDAGETIDINFO). |
usePrefetch |
boolean |
No | If true, prefetches the NID from the user's profile. |
nidTypeSource |
string |
No | Form field key that resolves the NID sub-type dynamically. |
preFetchChecks are configured and enabled.POST to /integration/v1/fetch/sync with endpoint code IREMBOHUB_VALIDATE_APPLICATION_PROPERTY_UNIQUENESS_FETCH and the payload:{
"serviceId": "<SERVICE_UUID>",
"propertyKey": "<PROPERTY_KEY>",
"propertyValue": "<USER_ENTERED_VALUE>",
"applicationStates": ["INITIATED", "PENDING", "..."]
}
data.unique is true: No duplicate found. Pipeline continues to the main data fetch.data.unique is false: A duplicate exists. The component:
messageTranslationKey with parameters extracted from the response via messageTranslationParams.errorKey) on the form control.Requirement: Before fetching land information by UPI, validate that the Plot UPI does not already have an active Water Connection Request application (any status except REJECTED).
{
"key": "Plotupi69Fu3",
"type": "customgenericguardeddatafetch",
"props": {
"url": "/integration/v1/fetch/sync",
"label": "Plot UPI",
"useBaseUrl": true,
"triggerType": "TRIGGER_BUTTON",
"endpointCode": "<MAIN_FETCH_ENDPOINT_CODE>",
"populates": ["..."],
"payloadKeyMappings": [
{
"key": "upi",
"isStatic": false,
"sourceField": "Plotupi69Fu3"
}
],
"preFetchChecks": [
{
"enabled": true,
"url": "/integration/v1/fetch/sync",
"endpointCode": "IREMBOHUB_VALIDATE_APPLICATION_PROPERTY_UNIQUENESS_FETCH",
"useBaseUrl": true,
"method": "POST",
"payloadKeyMappings": [
{
"key": "serviceId",
"isStatic": true,
"staticValue": "<WATER_CONNECTION_SERVICE_UUID>"
},
{
"key": "propertyKey",
"isStatic": true,
"staticValue": "Plotupi69Fu3"
},
{
"key": "propertyValue",
"isStatic": false,
"sourceField": "Plotupi69Fu3"
},
{
"key": "applicationStates",
"isStatic": true,
"staticValue": [
"INITIATED",
"PENDING",
"PENDING_APPROVAL",
"PENDING_PUSH_TO_PARTNER",
"IN_PROGRESS",
"APPROVED",
"COMPLETED",
"ON_HOLD"
]
}
],
"errorKey": "duplicateApplication",
"errorMessage": "The provided Plot UPI already has an active water connection application.",
"blockOnSuccess": {
"responseField": "data.unique",
"value": false,
"messageTranslationKey": "DUPLICATE_WATER_CONNECTION_UPI",
"messageTranslationParams": {
"applicationNumber": "data.existingApplication.applicationNumber"
}
}
}
]
},
"validation": {
"messages": {
"duplicateApplication": "The provided Plot UPI already has an active water connection application."
}
}
}
Add the following to your i18n translation files:
English (en.json):
{
"DUPLICATE_WATER_CONNECTION_UPI": "The provided Plot UPI already has an active water connection application (Application No. {{applicationNumber}}). You cannot submit a new application while this one is still in progress."
}
Kinyarwanda (rw.json):
{
"DUPLICATE_WATER_CONNECTION_UPI": "UPI y'Ikibanza mwatanze isanzwe ifite dosiye y'ubusabe bwo guhabwa amazi (Nomero ya dosiye: {{applicationNumber}}). Ntushobora gutanga ubundi busabe mu gihe hari ubundi butarasubizwa."
}
French (fr.json):
{
"DUPLICATE_WATER_CONNECTION_UPI": "La parcelle dont l'identifiant unique (UPI) est fourni possède déjà une demande de connexion d'eau en cours (Numéro du dossier : {{applicationNumber}}). Vous ne pouvez pas soumettre une nouvelle demande tant que celle-ci est encore en cours de traitement."
}
This example shows the pattern used for checking by NID/identification document number (from the original PR sample):
{
"key": "identificationDocumentNumber",
"type": "customgenericguardeddatafetch",
"props": {
"url": "/integration/v1/fetch/sync",
"label": "Identification document number",
"useBaseUrl": true,
"triggerType": "TRIGGER_BUTTON",
"endpointCode": "RSSB_GET_HOUSEHOLD_DETAILS_BY_NID",
"populates": ["..."],
"payloadKeyMappings": [
{
"key": "documentNumber",
"isStatic": false,
"sourceField": "identificationDocumentNumber"
}
],
"nidVerification": {
"enabled": true,
"idType": "NID",
"endpointCode": "NIDAGETIDINFO",
"usePrefetch": false
},
"preFetchChecks": [
{
"enabled": true,
"url": "/integration/v1/fetch/sync",
"endpointCode": "IREMBOHUB_VALIDATE_APPLICATION_PROPERTY_UNIQUENESS_FETCH",
"useBaseUrl": true,
"method": "POST",
"payloadKeyMappings": [
{
"key": "serviceId",
"isStatic": true,
"staticValue": "f17f077b-9997-4239-864d-35921fb237b0"
},
{
"key": "propertyKey",
"isStatic": true,
"staticValue": "identificationDocumentNumber"
},
{
"key": "propertyValue",
"isStatic": false,
"sourceField": "identificationDocumentNumber"
},
{
"key": "applicationStates",
"isStatic": true,
"staticValue": [
"PENDING_PUSH_TO_PARTNER"
]
}
],
"errorKey": "duplicateApplication",
"errorMessage": "A duplicate application was found.",
"blockOnSuccess": {
"responseField": "data.unique",
"value": false,
"messageTranslationKey": "DUPLICATE_APPLICATION_PROPERTY",
"messageTranslationParams": {
"applicationNumber": "data.existingApplication.applicationNumber",
"applicationState": "data.existingApplication.applicationState"
}
}
}
]
},
"validation": {
"messages": {
"duplicateApplication": "A duplicate application was found."
}
}
}
When setting up a new uniqueness check, ensure the following:
customgenericguardeddatafetch (not customgenericdatafetch)preFetchChecks[].enabled is set to trueserviceId in payloadKeyMappings matches the correct service UUIDpropertyKey matches the exact application property key in the service definitionsourceField points to the correct form field keyapplicationStates includes all relevant statuses to check againsterrorKey in preFetchChecks matches the key in validation.messagesDUPLICATE_WATER_CONNECTION_UPI) is added to all locale files (EN, RW, FR)messageTranslationParams paths match the actual API response structure| Symptom | Likely Cause | Fix |
|---|---|---|
| Check does not run at all | enabled is false or field type is customgenericdatafetch |
Set enabled: true and use customgenericguardeddatafetch |
| Error shows but application number is missing | messageTranslationParams path does not match API response |
Verify data.existingApplication.applicationNumber matches the actual response structure |
| Static fallback message shows instead of translated message | Translation key not found in i18n files | Add the translation key to all locale JSON files |
| Check passes when it should block | Missing statuses in applicationStates |
Ensure all relevant statuses are listed |
| Form field not blocked after failed check | errorKey does not match validation.messages key |
Ensure both use the same key (e.g., duplicateApplication) |
| Main fetch still runs after check failure | Multiple checks configured, wrong one failing | Verify the order and conditions of all checks in the array |
The IREMBOHUB_VALIDATE_APPLICATION_PROPERTY_UNIQUENESS_FETCH endpoint returns:
{
"data": {
"unique": true | false,
"existingApplication": {
"applicationNumber": "APP-2025-00123",
"applicationState": "PENDING_APPROVAL"
}
}
}
data.unique: true if no matching application found, false if a duplicate exists.data.existingApplication: Present when unique is false. Contains details of the existing application for use in error messages.