# Fix CORS Errors in ToolJet REST API Calls

> ToolJet CORS errors appear when a RunJS query or browser-side fetch hits an API that does not return Access-Control-Allow-Origin for your ToolJet domain. Route the call through a RealComposer pipeline: it handles the OPTIONS preflight, adds the right headers, and injects any upstream auth from the vault.

- Canonical page: https://realcomposer.com/recipes/fix-cors-error-tooljet
- Client platform: ToolJet
- Upstream services: REST API
- Category: Platforms & Frameworks
- JSON version: https://realcomposer.com/recipes/fix-cors-error-tooljet.json
- Install in RealComposer (free plan, pre-fills this pipeline): https://realcomposer.com/dashboard/pipelines/new?recipe=fix-cors-error-tooljet&via=agent

## Problem: Why ToolJet browser queries hit CORS blocks

Many internal and vendor APIs were never configured for cross-origin browser calls. When ToolJet code fetches them directly, the browser rejects the response before your query can read it, even though the same request works in cURL.

- Blocked responses on APIs without Access-Control-Allow-Origin
- Teams loosening CORS to * on internal APIs as a workaround
- Auth headers copied into RunJS to get around the block

## Solution steps

1. **Store credentials in Secret Vault**: Save UPSTREAM_API_TOKEN in RealComposer so the value never enters the ToolJet project.

2. **Configure the edge pipeline**: Add the upstream steps with vault header injection, set allowed origins, and map only the fields the screen binds.

3. **Point the ToolJet query at the edge URL**: Call your workspace edge URL with a restricted rc_live_ client token.

```
// ToolJet RunJS query
const res = await fetch('https://realcomposer.com/v1/workspace/fix-cors-error-tooljet', {
  headers: { Authorization: 'Bearer rc_live_client_token' }
});
return await res.json();
```

## Pipeline config

Import this config into a RealComposer pipeline. `{{secrets.NAME}}` values are read from the encrypted Secret Vault at request time, so the client never sees them.

```json
{
  "name": "Fix CORS Errors in ToolJet REST API Calls",
  "method": "GET",
  "cacheTtlSeconds": 0,
  "authMode": "client_token",
  "steps": [
    {
      "stepKey": "upstream",
      "upstreamUrlTemplate": "https://{{secrets.UPSTREAM_API_HOST}}/v1/orders",
      "httpMethod": "GET",
      "headersTemplate": {
        "Authorization": "Bearer {{secrets.UPSTREAM_API_TOKEN}}"
      },
      "timeoutMs": 5000
    }
  ],
  "outputMapping": {
    "version": 1,
    "fields": [
      {
        "targetPath": "orders",
        "sourceStep": "upstream",
        "sourcePath": "data"
      },
      {
        "targetPath": "total",
        "sourceStep": "upstream",
        "sourcePath": "meta.total"
      }
    ]
  }
}
```

## Call the pipeline

```bash
curl -X GET "https://realcomposer.com/v1/workspace/fix-cors-error-tooljet" \
  -H "Authorization: Bearer rc_live_client_token"
```

## Payload before and after

Raw upstream response:

```json
{
  "data": [
    {
      "id": "ord_1",
      "status": "shipped",
      "_links": {
        "self": "/v1/1"
      }
    }
  ],
  "meta": {
    "total": 1,
    "page": 1,
    "request_id": "req_88"
  }
}
```

Slimmed RealComposer response:

```json
{
  "orders": [
    {
      "id": "ord_1",
      "status": "shipped",
      "_links": {
        "self": "/v1/1"
      }
    }
  ],
  "total": 1
}
```

## FAQ

### Does ToolJet have its own CORS workaround?

Queries run through ToolJet data sources are executed by the ToolJet server, so they avoid browser CORS. RunJS fetches do not. RealComposer covers the browser case and also adds caching and slimming.

### Which origins should I allow?

Add your ToolJet Cloud or self-hosted app domain to the pipeline allowed origins and leave every other origin blocked.
