When an Ellucian Data Connect forEach is followed by a reducer, a JavaScript step that reads message.payload as if it still had the per iteration shape can get undefined or the wrong object instead of the expected array of results. The reducer stores iteration payloads under the configured accumulator path, so the next step must read that named property, such as payload.fullJobCandidates or payload.fullEmployeeCandidates, rather than payload.candidates.

After the reducer runs, message.payload contains the collected iteration results under the configured accumulator path. In the implementation covered here, reducers collect results under fullJobCandidates or fullEmployeeCandidates, and downstream code reads whichever collection is available with a ?? [] fallback.

This entry in our Ellucian Data Connect tips and implementation patterns series focuses on the payload shape produced by a reducer and how downstream JavaScript should access the collected iteration results.

How the reducer reshapes `message.payload`

Inside a forEach, processing operates on the payload shape for the current iteration. Once the reducer collects those iteration results, the downstream payload no longer has that same shape.

Conceptually:

forEach iteration payloads
A
B
C

Reducer

message.payload = {
    fullJobCandidates: [
        A,
        B,
        C
    ]
}

The important point is that the configured accumulator path determines where the collected iteration results are written in the output payload.

This is also separate from JavaScript Array.prototype.reduce(). Here, reducer refers to the Ellucian Data Connect pipeline step that collects iteration results.

Use the accumulator as the output key

The source pipeline uses reducer configurations such as:

{ "accumulator": "payload.fullJobCandidates" }

and:

{ "accumulator": "payload.fullEmployeeCandidates" }

With payload.fullJobCandidates configured as the accumulator, the resulting payload looks like:

{
    fullJobCandidates: [
        { /* iteration 1 result */ },
        { /* iteration 2 result */ },
        { /* iteration N result */ }
    ]
}

The accumulator therefore identifies the named location where the reducer places the collected iteration payloads. It does not simply concatenate results into an existing payload.candidates array.

This distinction matters when troubleshooting downstream JavaScript. A step that continues to assume the old payload path can appear to lose the reducer output even though the results were collected successfully.

Read the collected results in the next JavaScript step

The implementation reads the reducer output from the accumulator keys:

const candidates =
    payload.fullJobCandidates
 ?? payload.fullEmployeeCandidates
 ?? [];

After the job loop, the results are available under fullJobCandidates. After the employee loop, they are available under fullEmployeeCandidates.

The final ?? [] provides an empty array when both accumulator properties are null or undefined.

By contrast, this assumes the wrong post reducer shape:

const candidates = payload.candidates;

payload.candidates reflects the expected per iteration structure, not the payload produced by the reducer configuration in this workflow.

Key implementation details

  • The reducer accumulator determines the output location for iteration results.
  • After the reducer, message.payload no longer has the per iteration payload shape.
  • This implementation uses payload.fullJobCandidates and payload.fullEmployeeCandidates as accumulator paths.
  • Downstream JavaScript should read the appropriate accumulator property instead of assuming payload.candidates still exists.
  • Use ?? [] when an empty collection is the appropriate fallback.
  • Treat reducer output shape separately from related concerns such as concurrent sub-pipeline execution or audit message accumulation.

Related Data Connect posts

Need help with Ellucian Data Connect reducer and payload flows?

ABCloudz can help troubleshoot forEach and reducer behavior, trace changes in message.payload, configure accumulator paths, and design downstream processing for sub-pipeline, reporting, and Banner integration workflows.

Discuss your payload and reducer logic