# Part 2: Fetching Offchain Data
Source: https://docs.chain.link/cre/getting-started/part-2-fetching-data-ts
Last Updated: 2025-11-04

> For the complete documentation index, see [llms.txt](/llms.txt).

In Part 1, you successfully built and ran a minimal workflow. Now, it's time to connect it to the outside world. In this section, you will modify your workflow to fetch data from a public API using the CRE SDK's [`HTTPClient`](/cre/reference/sdk/http-client).

## What you'll do

- Add a new URL to your workflow's config file.
- Learn about the `runInNodeMode` helper for offchain operations.
- Write a new function to fetch data from the public [`api.mathjs.org`](https://api.mathjs.org/) API.
- Integrate the offchain data into your main workflow logic.

## Step 1: Update your configuration

First, you need to add the API endpoint to your workflow's configuration. This allows you to easily change the URL without modifying your TypeScript code.

Open the `config.staging.json` file in your `my-calculator-workflow` directory and add the `apiUrl` key. Your file should now look like this:

```json
{
  "schedule": "*/30 * * * * *",
  "apiUrl": "https://api.mathjs.org/v4/?expr=randomInt(1,101)"
}
```

This URL calls the public mathjs.org API and uses its `randomInt(min, max)` function to return a random integer between 1 and 100. Note that the upper bound is exclusive, so we use `101` to get values up to 100. The API returns the number as a raw string in the response body.

## Step 2: Understand the `runInNodeMode` pattern

Many offchain data sources are **non-deterministic**, meaning different nodes calling the same API might get slightly different answers due to timing, load balancing, or other factors. The `api.mathjs.org` API with the `randomInt` function is a perfect example—each call will return a different random number.

The CRE SDK solves this with `runtime.runInNodeMode`, a helper function that transforms these potentially varied results into a single, highly reliable result. It works like a "map-reduce" for the DON:

1. **Map**: You provide a function (e.g., `fetchMathResult`) that will be executed by every node in the DON independently. Each node "maps" the offchain world by fetching its own version of the data.
2. **Reduce**: You provide a consensus algorithm (e.g., [`consensusMedianAggregation`](/cre/reference/sdk/consensus#consensusmedianaggregationt)) that takes all the individual results and "reduces" them into a single, trusted outcome.

This pattern is fundamental to securely and reliably bringing offchain data into your workflow.

> **NOTE: Learn More**
>
> The `runInNodeMode` helper enables node-level execution. To learn more about the distinction between the DON-level
> `Runtime` and node-level execution, see the [Runtime](/cre/reference/sdk/core/#runtime-and-noderuntime) section in the
> SDK Reference.

## Step 3: Add the HTTP fetch logic

Now, let's modify your `main.ts` file. You will add a new function, `fetchMathResult`, that contains the logic for calling the API. You'll also update the `onCronTrigger` function to call the `runInNodeMode` helper.

Replace the entire content of `onchain-calculator/my-calculator-workflow/main.ts` with the following code.

Code snippet for onchain-calculator/my-calculator-workflow/main.ts:

```typescript
import {
  CronCapability,
  HTTPClient,
  handler,
  consensusMedianAggregation,
  Runner,
  type NodeRuntime,
  type Runtime,
} from "@chainlink/cre-sdk"

type Config = {
  schedule: string
  apiUrl: string
}

type MyResult = {
  result: bigint
}

const initWorkflow = (config: Config) => {
  const cron = new CronCapability()

  return [handler(cron.trigger({ schedule: config.schedule }), onCronTrigger)]
}

// fetchMathResult is the function passed to the runInNodeMode helper.
// It contains the logic for making the request and parsing the response.
const fetchMathResult = (nodeRuntime: NodeRuntime<Config>): bigint => {
  const httpClient = new HTTPClient()

  const req = {
    url: nodeRuntime.config.apiUrl,
    method: "GET" as const,
  }

  // Send the request using the HTTP client
  const resp = httpClient.sendRequest(nodeRuntime, req).result()

  // The mathjs.org API returns the result as a raw string in the body.
  // We need to parse it into a bigint.
  const bodyText = new TextDecoder().decode(resp.body)
  const val = BigInt(bodyText.trim())

  return val
}

const onCronTrigger = (runtime: Runtime<Config>): MyResult => {
  runtime.log("Hello, Calculator! Workflow triggered.")
  // Use runInNodeMode to execute the offchain fetch.
  // The API returns a random number, so each node can get a different result.
  // We use median consensus to find a single, trusted value.
  const result = runtime.runInNodeMode(fetchMathResult, consensusMedianAggregation())().result()

  runtime.log(`Successfully fetched and aggregated math result: ${result}`)

  return {
    result,
  }
}

export async function main() {
  const runner = await Runner.newRunner<Config>()
  await runner.run(initWorkflow)
}
```

<Aside type="tip" title="Optional: Add runtime validation">
  If you added Zod validation in Part 1, you can continue using it here by replacing the `Config` type definition with a Zod schema and passing `configSchema` to `Runner.newRunner<Config>({ configSchema })`. See [Part 1: Optional Runtime Validation](/cre/getting-started/part-1-project-setup#optional-runtime-validation) for details.
</Aside>

> **NOTE: The .result() method**
>
> You'll notice that capability calls like `httpClient.sendRequest()` and `runtime.runInNodeMode()` use `.result()` to
> get the response. This is because traditional `async/await` doesn't work in the WebAssembly environment where CRE
> workflows run. The `.result()` method provides a way to handle asynchronous operations within WASM's synchronous
> execution model. For a deeper explanation, see [Understanding the `.result()`
> Pattern](/cre/reference/sdk/core-ts#understanding-the-result-pattern).

> **NOTE: Consensus & Aggregation**
>
> Because the `api.mathjs.org/v4/?expr=randomInt(1,101)` endpoint is non-deterministic, each node in the DON will receive a different random number. To create a single, reliable result from these varied responses, we use [`consensusMedianAggregation`](/cre/reference/sdk/consensus#consensusmedianaggregationt).

This algorithm collects the numerical results from all nodes, sorts them, and selects the median value. This approach is robust against outliers and provides a trustworthy, aggregated number for your workflow to use, even when the underlying data source is not consistent across calls.

**In local simulation**: The simulator uses a single-node model for faster testing. The "consensus" step still occurs, but involves one node wrapping its result in a standardized report structure. When deployed, true multi-node Byzantine Fault Tolerant (BFT) consensus is performed across all capability operations.

To learn more about how consensus works in CRE, see [Consensus Computing](/cre/concepts/consensus-computing) and the [Consensus & Aggregation reference](/cre/reference/sdk/consensus).

## Step 4: Run the simulation and review the output

Run the `simulate` command from your project root directory (the `onchain-calculator/` folder). Because there is only one trigger, the simulator runs it automatically.

```bash
cre workflow simulate my-calculator-workflow --target staging-settings
```

The output shows the new user logs from your workflow, followed by the final `Workflow Simulation Result`.

```bash
Workflow compiled
2025-11-03T19:05:41Z [SIMULATION] Simulator Initialized

2025-11-03T19:05:41Z [SIMULATION] Running trigger trigger=cron-trigger@1.0.0
2025-11-03T19:05:41Z [USER LOG] Hello, Calculator! Workflow triggered.
2025-11-03T19:05:41Z [USER LOG] Successfully fetched and aggregated math result: 60

Workflow Simulation Result:
 {
  "result": 60
}

2025-11-03T19:05:41Z [SIMULATION] Execution finished signal received
2025-11-03T19:05:41Z [SIMULATION] Skipping WorkflowEngineV2
```

- **`[USER LOG]`**: You can now see both of your `runtime.log()` calls in the output. The second log shows the fetched value (e.g., `60`), confirming that the API call and consensus worked correctly.
- **`[SIMULATION]`**: These are system-level messages from the simulator showing its internal state.
- **`Workflow Simulation Result`**: This is the final return value of your workflow. The `result` field now contains the aggregated value from the API as a number.

Your workflow can now fetch and process data from an external source.

## Next Steps

Next, you'll learn how to interact with a smart contract to read data from the blockchain and combine it with this offchain result.

- **[Part 3: Reading an Onchain Value](/cre/getting-started/part-3-reading-onchain-value)**