> For the complete documentation index, see [llms.txt](https://docs.coherent.global/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.coherent.global/spark-apis/transforms-api/nodejs22.md).

# Nodejs22

{% hint style="info" %}
This feature is only enabled on certain environments and tenants. [Contact support](/support/contact-support.md) for more information about this feature.
{% endhint %}

With this transform type, you can customize the request and response with access to [Node.js v22](https://nodejs.org/docs/latest-v22.x/api/) to program actions for each call to the transform endpoint in [JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript).

We recommend this transform type if you have complex requirements involving multiple API calls. Some examples of how this could be used include:

* Combine multiple [Execute API](/spark-apis/execute-api.md) calls together.
* Apply [JSONtransforms](/spark-apis/transforms-api/jsontransforms.md) for multiple API calls.
* Orchestrate other [Spark APIs](/spark-apis/getting-started.md) to facilitate data transformation and processing.
* Make external API calls.
* Develop tools that are not even Spark related.

For this particular transform type, the [npm](https://www.npmjs.com/) packages for  [`fast-xml-parser`](https://www.npmjs.com/package/fast-xml-parser) and [`jsonata`](https://www.npmjs.com/package/jsonata/) are available for enhanced JSON and XML parsing and composition.

```mermaid
flowchart LR
    Request[/System request/]
    Transform["Call the Spark<br/>/transform endpoint"]
    Doc["Fetch Transform document"]
    Response[/Spark response/]

    Request --> Transform --> Doc

    subgraph Runtime["Transform runtime"]
        direction TB
        Code["Run the transform code"]
        Execute1["Call API ..."]
        Execute2["Call API ..."]

        Code -.-> Execute1
        Code -.-> Execute2
    end

    Doc --> Code
    Code --> Response
```

## Transform document

Transform documents are used to define the code relating to a transform. For `Nodejs22`, the transform document is a JSON document defined by the following objects:

| Key                                  | Value                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               |
| ------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `transform_type`                     | `Nodejs22_v1.0.0`                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   |
| `transform_code`                     | <p>Single string containing the code for the transform code.</p><p>The code must contain the function <code>async function handler(event) {}</code>.</p><p>When the transform is invoked, this function is called. Parameters from the transform request are passed into the <code>event</code> parameter as an object. This function should return:</p><ul><li><code>http\_status\_code</code> for the HTTP status code.</li><li><code>headers</code> for response headers.</li><li><code>body</code> for the response body.</li></ul><p>This design pattern follows the <a href="https://docs.aws.amazon.com/lambda/">AWS Lambda</a> <a href="https://docs.aws.amazon.com/lambda/latest/dg/lambda-nodejs.html">Define Lambda function handler in Node.js</a>.</p> |
| `transform_log_level`                | <p>Optional parameter.<br>This controls the level of logging that will be recorded during the execution.</p><ul><li>This can take the values of <code>TRACE</code>, <code>DEBUG</code>, <code>INFO</code>, <code>WARN</code>, <code>ERROR</code>.</li></ul>                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         |
| `transform_log_visible`              | <p><strong>NOTE: This configuration is not available yet.</strong></p><p>Optional parameter.</p><p>This controls whether the execution logs will be made available in a link through the response headers. These can be used for reviewing the execution and debugging.</p><p>If omitted, the default is <code>false</code>.</p>                                                                                                                                                                                                                                                                                                                                                                                                                                    |
| `transform_allowed_external_domains` | <p>Optional parameter.<br>Array containing a list of external domains that the transform is allowed to access during the course of execution. These domains must be resolvable using <a href="https://www.nslookup.io/">NsLookup</a>.</p>                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           |

### Create the transform document

The transform document can be manually created and uploaded to Spark using the **New transforms** featur&#x65;*.*

1. The `transform_type` is `Nodejs22_v1.0.0`.
2. The `transform_code` must be a single string containing the code for the transform.
   1. If you are using either `fast-xml-parser` or `jsonata`, make sure to include the [`require()`](https://nodejs.org/docs/latest-v22.x/api/modules.html#requireid) statements to import the modules the respective modules.
   2. Remove any lines containing comments.
   3. Any double quotation marks `"` need to be escaped where `"` is replaced with `\"`.
      * Note that JavaScript allows for single quotation marks `'` to be interchanged with double quotation marks `"`. If `'` are used exclusively in lieu of `"`, then the escape character is not needed.
   4. If you are using [Visual Studio Code](https://code.visualstudio.com/), you can use the **Join lines** feature to make it easy to combine multiple lines into a single string.
      1. Select the transform text that you would like to combine onto a single line.
         1. Access the **Command Palette** with the keyboard shortcut `F1` or `Ctrl+Shift+P` on Windows or `Shift+⌘+P` on Mac.
         2. Type in the command **Join lines** and select it.
   5. As an alternative to **Join lines**, line breaks `Ctrl+Enter` can be replaced with the new line character `\n`.
3. Save the document as a `JSON` file. The filename must end with `_transform.json`.
4. Follow the instructions in [Folder overview](/navigation/folder-overview.md#new-transform) to upload the transform document to Spark.

### Sample transform document

<details>

<summary></summary>

```json
{
  "transform_type": "Nodejs22_v1.0.0",
  "transform_code": "async function handler(event) { return { http_status_code: 200, headers: { \"Content-Type\": \"application/json\", myresponseheader: \"Hello, World!\", }, body: { myresponsebody: \"Hello, World!\", }, } }"
}
```

</details>

## Transforms API

{% code overflow="wrap" %}

```shellscript
# Transform document applies to a specific Spark service
POST /{tenant}/api/v4/transforms/{transform_folder}/{transform}/for/{folder}/{service}
# Transform document does not apply to a specific Spark service
POST /{tenant}/api/v4/transforms/{transform_folder}/{transform}/run
```

{% endcode %}

### Authorization

* `Bearer {token}` accessible from [Authorization - Bearer token](/spark-apis/authorization-bearer-token.md) or systematically via [Client credentials](/identity-and-access-management/client-credentials.md).
  * The request headers should include a key for `Authorization` with the value `Bearer {token}`.
* API key created from [Authorization - API keys](/spark-apis/authorization-api-keys.md).
  * The request headers should include the keys `x-synthetic-key` and `x-tenant-name` with the values of the API key and tenant name respectively.

Note that the authorization must include permissions that can access:

* The requested Spark service.
* The requested transform.
  * Either the folder that the transform is stored OR
  * If the transform is in the `apps/transforms` folder. This would typically require `user:pf` or `supervisor:pf`.

### Path parameters

| Key                   | Value                                                                                                                     |
| --------------------- | ------------------------------------------------------------------------------------------------------------------------- |
| `tenant` \*           | Tenant is part of your sign-in URL and also available in the [Navigation menu](/navigation/navigation-menu.md#user-menu). |
| `transform_folder` \* | Folder associated with the transform document of the request.                                                             |
| `transform` \*        | Transform document associated with the transform document of the request (without `_transform.json`).                     |
| `folder` \*           | Folder associated with the Spark service of the request.                                                                  |
| `service` \*          | Service associated with the Spark service of the request.                                                                 |

### Request headers

<table data-full-width="false"><thead><tr><th>Key</th><th>Value</th></tr></thead><tbody><tr><td><code>x-meta</code> or <code>x-request-meta</code></td><td>Optional parameter.<br>This can be used to introduce key-value pairs into the <code>event.context</code> parameter object. See <a data-mention href="#examine-the-event-parameter-object">#examine-the-event-parameter-object</a>for more details.<br>Example:<br><code>"{\"call_purpose\":\"Transform mainframe request\"}"</code></td></tr><tr><td><code>Content-Type</code></td><td><code>application/json</code>, <code>application/xml</code>, or <code>text/xml</code></td></tr><tr><td>+ any other request headers...</td><td>These will be passed into <code>event.request.headers</code> as key-value pairs. See <a data-mention href="#examine-the-event-parameter-object">#examine-the-event-parameter-object</a>for more details.</td></tr></tbody></table>

### Request body

`Content-Type: application/json`  or `Content-Type: application/xml`

### HTTP status code

This is determined by the `handler()` function `return{}` of `http_status_code` object.

This can also be defined with a response header `x-spark-transform-response-httpstatuscode` .

### Response headers

This is determined by the `handler()` function `return{}` of the `headers()` object. We would suggest at the minimum to return the appropriate `Content-Type` for your response data.

### Response body

This is determined by the `handler()` function `return{}` of the `body()` object.

### Limitations

* The API call has a maximum lifetime of `120 s`.
* API calls can only be made to the `coherent.global` domain unless defined by `transform_allowed_external_domains`.

<details>

<summary>Restricted <a href="https://nodejs.org/docs/latest-v22.x/api/">Node.js v22 modules</a></summary>

| Restricted module   | Restricted functionality                               |
| ------------------- | ------------------------------------------------------ |
| `fs`                | Read/write access to the file system.                  |
| `child_process`     | Run arbitrary system commands (`exec`, `spawn`, etc.). |
| `net` / `dgram`     | Create raw TCP/UDP sockets.                            |
| `http`, `https`     | Make network requests. Use `fetch` instead.            |
| `vm`                | Create a nested sandbox.                               |
| `process`           | Access environment variables or exit the process.      |
| `os`                | Access system information.                             |
| `cluster`           | Fork additional worker processes.                      |
| `worker_threads`    | Run threads outside of the memory or sandbox space.    |
| `require` (general) | Loading of unsupported modules.                        |

</details>

### Known errors

Sometimes code will work locally but fail to run on Spark. Some examples where this may occur:

* If you try to return all of the headers from the `/execute` API call [#emulate-a-passthrough-to-the-execute-api-v3](#emulate-a-passthrough-to-the-execute-api-v3 "mention") to the `/transforms` response headers, it may fail due to content encoding discrepancies.

## Examples

### Start with a Hello, World! transform

The response includes a custom header and response body with the message `Hello, World!`.

{% tabs %}
{% tab title="JavaScript" %}

<details>

<summary></summary>

```javascript
async function handler(event) {
    return {
        http_status_code: 200,
        headers: {
            "Content-Type": "application/json",
            myresponseheader: "Hello, World!",
        },
        body:
            {
                myresponsebody: "Hello, World!",
            },
    }
}
```

</details>
{% endtab %}

{% tab title="Transform document" %}

<details>

<summary></summary>

```json
{
  "transform_type": "Nodejs22_v1.0.0",
  "transform_code": "async function handler(event) { return { http_status_code: 200, headers: { \"Content-Type\": \"application/json\", myresponseheader: \"Hello, World!\", }, body: { myresponsebody: \"Hello, World!\", }, } }"
}
```

</details>

{% file src="/files/5HO7iHI95tPl6NOPu7RC" %}
{% endtab %}

{% tab title="Sample request" %}

* The request is made to the transform `mytransformsfolder/HelloWorld`.
* The `/run` endpoint is called because this does not relate to a particular service.

<details>

<summary></summary>

```sh
curl --location 'https://excel.myenvironment.coherent.global/mytenant/api/v4/transforms/mytransformsfolder/HelloWorld/run' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {token}' \
--data ''
```

</details>
{% endtab %}

{% tab title="Sample response" %}

<details>

<summary></summary>

`HTTP 200 OK` `Content-Type: application/json`

```json
{
    "myresponsebody": "Hello, World!"
}
```

</details>
{% endtab %}
{% endtabs %}

### Examine the `event` parameter object

When the `handler()` function is called, the `event` parameter is used to get information about the request details. These can be used as inputs for the program.

{% tabs %}
{% tab title="JavaScript" %}

<details>

<summary></summary>

```javascript
async function handler(event) {
    return {
        http_status_code: 200,
        headers: { "Content-Type": "application/json" },
        body: { event }
    };
}
```

</details>
{% endtab %}

{% tab title="Transform document" %}

<details>

<summary></summary>

```json
{
  "transform_type": "Nodejs22_v1.0.0",
  "transform_code": "async function handler(event) { return { http_status_code: 200, headers: { \"Content-Type\": \"application/json\" }, body: { event } }; }"
}
```

</details>

{% file src="/files/l4SygfUZOxjyBVNA2HyL" %}
{% endtab %}

{% tab title="Sample request" %}

* The request is made to the transform `mytransformsfolder/EventViewer`.
* The `/run` endpoint is called because this does not relate to a particular service.

<details>

<summary></summary>

```javascript
curl --location 'https://excel.myenvironment.coherent.global/mytenant/api/v4/transforms/mytransformsfolder/EventViewer/for/myfolder/myservice' \
--header 'Content-Type: application/json' \
--header 'myrequestheaderkey: myrequestheadervalue' \
--header 'x-meta: "{\"call_purpose\":\"Event viewer\"}"' \
--header 'Authorization: Bearer {Token}' \
--data '{
    "request_data": {
        "inputs": {
            "A": 1,
            "B": 2
        }
    }
}'
```

</details>
{% endtab %}

{% tab title="Sample response" %}

<details>

<summary></summary>

`HTTP 200 OK` `Content-Type: application/json`

```json
{
    "event": {
        "request": {
            "body": {
                "request_data": {
                    "inputs": {
                        "A": 1,
                        "B": 2
                    }
                }
            },
            "headers": {
                "Accept": "*/*",
                "Host": "excel.myenvironment.coherent.global",
                "User-Agent": "curl/7.88.1",
                "Accept-Encoding": "gzip, deflate, br",
                "Authorization": "Bearer {Token}",
                "Content-Type": "application/json",
                "Content-Length": 107,
                "X-Forwarded-For": "129.6.15.28",
                "X-Forwarded-Proto": "https",
                "X-Forwarded-Port": 443,
                "X-Trace-Id": "Root=1-6839b403-09c360963bf7aec943477e9g",
                "myrequestheaderkey": "myrequestheadervalue",
                "x-meta": "{\"call_purpose\":\"Event viewer\",\"correlation_id\":\"91496134-6357-4bc7-a230-f9ad31855254\"}"
            }
        },
        "whitelisted_domains": [
            "*.coherent.global"
        ],
        "total_allowed_run_time_ms": 60000,
        "context": {
            "tenant": "coherent",
            "service_uri": "/folders/myfolder/services/myservice",
            "call_purpose": "Event viewer",
            "correlation_id": "91496134-6357-4bc7-a230-f9ad31855254",
            "spark_url": "https://excel.myenvironment.coherent.global",
            "log_level": ""
        },
        "secrets": {
            "authorization": "Bearer {Token}"
        }
    }
}
```

</details>
{% endtab %}
{% endtabs %}

### Emulate a passthrough to the Execute API (v3)

The transform endpoint acts as a passthrough to send inputs to the [Execute API (v3)](/spark-apis/execute-api/execute-api-v3.md). USe this to build transforms that make API calls to Spark.

{% tabs %}
{% tab title="JavaScript" %}

* Note the `encodeURI()` function that is used in the `fetchResource` declaration. This is needed because folders or services that contain a space in the name need to be encoded to `%20` in order for [`fetch()`](https://nodejs.org/en/learn/getting-started/fetch) to work.
* The `Authorization` to the `/execute` API call is handled by using the token available in `event.secrets.authorization`.
* Trying to return all of the headers from the `/execute` call may lead to errors. See [#known-errors](#known-errors "mention").

<details>

<summary></summary>

```javascript
async function handler(event) {

    const fetchResource = `${event.context.spark_url}/${event.context.tenant}/api/v3${encodeURI(event.context.service_uri)}/execute`;

    const fetchOptions = {
        method: "POST",
        headers: {
            Authorization: event.secrets.authorization,
            "Content-Type": "application/json",
            "x-tenant-name": event.context.tenant,
        },
        body: JSON.stringify(event.request.body),
    };

    const response = await fetch(fetchResource, fetchOptions);
    const responseBody = await response.json();

    return {
        http_status_code: response.status,
        headers: { "Content-Type": response.headers.get("content-type") },
        body: responseBody,
    };
}
```

</details>
{% endtab %}

{% tab title="Transform document" %}

<details>

<summary></summary>

```json
{
  "transform_type": "Nodejs22_v1.0.0",
  "transform_code": "async function handler(event) { const fetchResource = `${event.context.spark_url}/${event.context.tenant}/api/v3${encodeURI(event.context.service_uri)}/execute`; const fetchOptions = { method: \"POST\", headers: { Authorization: event.secrets.authorization, \"Content-Type\": \"application/json\", \"x-tenant-name\": event.context.tenant, }, body: JSON.stringify(event.request.body), }; const response = await fetch(fetchResource, fetchOptions); const responseBody = await response.json(); return { http_status_code: response.status, headers: { \"Content-Type\": response.headers.get(\"content-type\") }, body: responseBody, }; }"
}
```

</details>

{% file src="/files/gLRFhvpxkQFpqOv2Nhah" %}
{% endtab %}

{% tab title="Sample request" %}

* The request is made to the transform `mytransformsfolder/EventViewer` and the service `myfoldername/AplusB`.
* The endpoint includes a reference to `myfolder/myservice` which will be the target for this API call.

<details>

<summary></summary>

```sh
curl --location 'https://excel.myenvironment.coherent.global/mytenant/api/v4/transforms/mytransformsfolder/v3Execute/for/myfolder/myservice' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {token}' \
--data '{
    "request_data": {
        "inputs": {
            "A": 1,
            "B": 2
        }
    }
}'
```

</details>
{% endtab %}

{% tab title="Sample response" %}

<details>

<summary></summary>

`HTTP 200 OK` `Content-Type: application/json`

```json
{
    "status": "Success",
    "response_data": {
        "outputs": {
            "AplusB": 3
        },
        "warnings": null,
        "errors": null,
        "service_chain": null
    },
    "response_meta": {
        "service_id": "8b750c74-9e33-470f-8c2c-4173970112c7",
        "version_id": "cb577a09-12a6-412d-99d5-2b6a6aba794c",
        "version": "0.3.0",
        "process_time": 1,
        "call_id": "6798bf8e-1827-4cfd-ab0f-b3bef11adf02",
        "compiler_type": "Neuron",
        "compiler_version": "1.22.8",
        "source_hash": null,
        "engine_id": "C70FF0EF6EF466B551756ADE3289475B",
        "correlation_id": "",
        "parameterset_version_id": null,
        "system": "SPARK",
        "request_timestamp": "2025-05-29T11:07:51.132Z"
    },
    "error": null
}

```

</details>
{% endtab %}
{% endtabs %}

### Transform JSON with JSONata

[JSONata](https://jsonata.org) is a "lightweight query and transformation language for JSON data". You can test your JSONata transforms in the [JSONata Exerciser](https://try.jsonata.org/).

The `jsonata` package is included with the `Nodejs22` transform to enable extensive JSON parsing capabilities. Note the `jsonata` package version used in [Update roadmap](/spark-apis/transforms-api/update-roadmap.md).

Example is based on [Example nodejs application](https://docs.jsonata.org/using-nodejs#example-nodejs-application).

{% tabs %}
{% tab title="JavaScript" %}

<details>

<summary></summary>

{% code overflow="wrap" %}

```javascript
const jsonata = require("jsonata");

async function handler(event) {
    const data = {
        example: [
            { value: 4 },
            { value: 7 },
            { value: 13 }
        ]
    };
    const expression = jsonata("$sum(example.value)");
    const result = await expression.evaluate(data);
    return { http_status_code: 200, headers: { "Content-Type": "application/json" }, body: { sum: result } };
}
```

{% endcode %}

</details>
{% endtab %}

{% tab title="Transform document" %}

<details>

<summary></summary>

```json
{
  "transform_type": "Nodejs22_v1.0.0",
  "transform_code": "const jsonata = require(\"jsonata\"); async function handler(event) { const data = { example: [ { value: 4 }, { value: 7 }, { value: 13 } ] }; const expression = jsonata(\"$sum(example.value)\"); const result = await expression.evaluate(data); return { http_status_code: 200, headers: { \"Content-Type\": \"application/json\" }, body: { sum: result } }; }"
}
```

</details>

{% file src="/files/X8zy7ReuDSxk7uw2GRIZ" %}
{% endtab %}

{% tab title="Sample request" %}

* The request is made to the transform `mytransformsfolder/JSONata`.
* The `/run` endpoint is called because this does not relate to a particular service.

<details>

<summary></summary>

```sh
curl --location 'https://excel.myenvironment.coherent.global/mytenant/api/v4/transforms/mytransformsfolder/JSONata/run' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {token}' \
--data ''
```

</details>
{% endtab %}

{% tab title="Sample response" %}

<details>

<summary></summary>

`HTTP 200 OK` `Content-Type: application/json`

```json
{
    "sum": 24
}
```

</details>
{% endtab %}
{% endtabs %}

### Transform XML with `fast-xml-parser`

[`fast-xml-parser`](https://github.com/NaturalIntelligence/fast-xml-parser) is the most popular package to manipulate XML in JavaScript.

The package is included with the `Nodejs22` transform to enable XML parsing capabilities. Note the `fast-xml-parser` package version used in [Update roadmap](/spark-apis/transforms-api/update-roadmap.md).

This example is based on [Builder\_v1.md](https://github.com/NaturalIntelligence/fast-xml-builder/blob/main/docs/Builder_v1.md#arraynodename).

{% tabs %}
{% tab title="JavaScript" %}

<details>

<summary></summary>

<pre class="language-javascript"><code class="lang-javascript"><strong>const fastXmlParser = require("fast-xml-parser");
</strong><strong>
</strong>async function handler(event) {
    const { XMLBuilder } = require("fast-xml-parser");

    const XmlBuilderOptions = {
        ignoreAttributes: false,
        format: true,
        arrayNodeName: "car"
    };

    const cars = [
        {
            "color": "purple",
            "type": "minivan",
            "registration": "2020-02-03",
            "capacity": 7
        },
        {
            "color": "orange",
            "type": "SUV",
            "registration": "2021-05-17",
            "capacity": 4
        },
    ];

    const builder = new XMLBuilder(XmlBuilderOptions);
    const output = builder.build(cars);

    return {
        http_status_code: 200,
        headers: {
            'Content-Type': 'application/xml'
        },
        body: output,
    };
}
</code></pre>

</details>
{% endtab %}

{% tab title="Transform document" %}

<details>

<summary></summary>

```json
{
  "transform_type": "Nodejs22_v1.0.0",
  "transform_code": "const fastXmlParser = require(\"fast-xml-parser\"); async function handler(event) { const { XMLBuilder } = require(\"fast-xml-parser\"); const XmlBuilderOptions = { ignoreAttributes: false, format: false, arrayNodeName: \"car\" }; const cars = [ { \"color\": \"purple\", \"type\": \"minivan\", \"registration\": \"2020-02-03\", \"capacity\": 7 }, { \"color\": \"orange\", \"type\": \"SUV\", \"registration\": \"2021-05-17\", \"capacity\": 4 }, ]; const builder = new XMLBuilder(XmlBuilderOptions); const output = builder.build(cars); return { http_status_code: 200, headers: { 'Content-Type': 'application/xml' }, body: output, }; }"
}
```

</details>

{% file src="/files/bZkZTKmVGO2J01GejaIb" %}
{% endtab %}

{% tab title="Sample request" %}

* The request is made to the transform `mytransformsfolder/XML`.
* The `/run` endpoint is called because this does not relate to a particular service.

<details>

<summary></summary>

```sh
curl --location 'https://excel.myenvironment.coherent.global/mytenant/api/v4/transforms/mytransformsfolder/XML/run' \
--header 'Content-Type: application/xml' \
--header 'Authorization: Bearer {token}' \
--data ''
```

</details>
{% endtab %}

{% tab title="Sample response" %}

<details>

<summary></summary>

`HTTP 200 OK` `Content-Type: application/xml`

```xml
<car>
    <color>purple</color>
    <type>minivan</type>
    <registration>2020-02-03</registration>
    <capacity>7</capacity>
</car>
<car>
    <color>orange</color>
    <type>SUV</type>
    <registration>2021-05-17</registration>
    <capacity>4</capacity>
</car>
```

</details>
{% endtab %}
{% endtabs %}

### Make an external API call

Extract the contents from the `www.google.com` homepage.

{% tabs %}
{% tab title="JavaScript" %}

<details>

<summary></summary>

```javascript
async function handler(event) {

    const fetchResource = "https://www.google.com";
    const fetchOptions = {method: "GET"};

    const response = await fetch(fetchResource, fetchOptions);
    const responseText = await response.text();
    const responseBody = {outputs: { page_content: responseText } };
    return {
        http_status_code: response.status,
        headers: { "content-type": "application/json" },
        body: responseBody,
    };
}
```

</details>
{% endtab %}

{% tab title="Transform document" %}

<details>

<summary></summary>

```json
{
  "transform_type": "Nodejs22_v1.0.0",
  "transform_code": "async function handler(event) { const fetchResource = \"https://www.google.com\"; const fetchOptions = {method: \"GET\"}; const response = await fetch(fetchResource, fetchOptions); const responseText = await response.text(); const responseBody = {outputs: { page_content: responseText } }; return { http_status_code: response.status, headers: { \"content-type\": \"application/json\" }, body: responseBody, }; }",
  "transform_allowed_external_domains": ["www.google.com"]
}
```

</details>

{% file src="/files/gxvQlGZ11DZlyAqr4DUH" %}
{% endtab %}

{% tab title="Sample request" %}

* The request is made to the transform `mytransformsfolder/HeyGoogle`.
* The `/run` endpoint is called because this does not relate to a particular service.

<details>

<summary></summary>

```sh
curl --location 'https://excel.myenvironment.coherent.global/mytenant/api/v4/transforms/mytransformsfolder/HeyGoogle/run' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {token}' \
--data ''
```

</details>
{% endtab %}

{% tab title="Sample response" %}

<details>

<summary></summary>

`HTTP 200 OK` `Content-Type: application/json`

```json
{
    "outputs": {
        "page_content":"<!doctype html><html itemscope=\"\" itemtype=\"http://schema.org/WebPage\" lang=\"en-SG\"></html>"
    }
}
```

</details>
{% endtab %}
{% endtabs %}

## Developer tools

JavaScript developers can use the [Node Transform Document Builder](https://github.com/floherent/node-transform-builder) which facilitates the build and validation of transform documents.

Alternatively, you can use this method to run code locally if you have Node.js installed. This process similar to [Building Lambda functions with Node.js](https://docs.aws.amazon.com/lambda/latest/dg/lambda-nodejs.html).

1. Decide how you would like to construct the request to the `/transforms` endpoint, including the structure of the request line, as well as request and response headers and body.
2. Create the transform described in [#examine-the-event-parameter-object](#examine-the-event-parameter-object "mention"). Run the proposed `/transforms` request against it and from the response copy the `event` object.
3. Test your script locally using the `runHandler.js` script below.
   1. Paste in the `event` object from above into `TEST_EVENT` of `runHandler.js`.
   2. Work on the `handler()` function in `runHandler.js`.
   3. If your script calls other [Spark APIs](/spark-apis/getting-started.md), your `Authorization` may expire as you develop the program. You may need to update your `event` object periodically with a new bearer token.
   4. Test your script in your terminal using the command `node runHandler.js`. It will print the `handler()` function `return`.
4. Compose and upload the transform document using the directions in [#transform-document](#transform-document "mention").
5. Test API calls against the `/transforms` endpoint to make sure you are getting the same result as the `runHandler.js` script.

{% file src="/files/qdht44ZL1puy2k8YlG3O" %}
