Understanding CORS in TelemetryOS Applications

Critical information about Cross-Origin Resource Sharing (CORS) and how it affects your applications running in browser contexts.

Understanding CORS in TelemetryOS Applications

Critical information about Cross-Origin Resource Sharing (CORS) and how it affects your applications.

What is CORS?

CORS (Cross-Origin Resource Sharing) is a browser security mechanism that controls which websites can make requests to your application or API.

The Problem

Web browsers enforce the Same-Origin Policy for security:

Your App Origin:     https://your-app.telemetryos.com
External API:        https://api.weather.com

Without CORS approval: ❌ Browser blocks the request
With CORS approval:    ✅ Browser allows the request

An "origin" consists of:

  • Protocol (https://)
  • Domain (api.weather.com)
  • Port (:443 for HTTPS)

Requests to a different origin require CORS permission from the target server.

Why CORS Matters for TelemetryOS

TelemetryOS applications run in browser contexts:

  1. On Devices - Applications run in iframes on TVs, kiosks, displays
  2. In Studio - Settings mount points run in admin portal iframes
  3. Local Development - Applications run in your browser during tos serve

All of these contexts enforce CORS.

Common CORS Error

Access to fetch at 'https://api.example.com/data' from origin
'https://your-app.telemetryos.com' has been blocked by CORS policy:
No 'Access-Control-Allow-Origin' header is present on the requested resource.

This error means:

  • The external API doesn't allow requests from your application's origin
  • The browser blocked the request for security
  • You cannot bypass this in client-side code

When CORS Blocks Your Requests

Scenario 1: Public API Without CORS

// This will likely fail with CORS error
const response = await fetch('https://api.publicdata.com/stats');

Why: Many public APIs don't enable CORS because they expect server-side usage only.

Scenario 2: Private API Without CORS

// This will fail with CORS error
const response = await fetch('https://your-company-api.com/data');

Why: Your own API might not have CORS configured if it was built for server-to-server communication.

Scenario 3: API with Restricted CORS

// API only allows requests from example.com
// Your telemetryos.com origin is blocked
const response = await fetch('https://partner-api.com/data');

Why: API configured CORS to only allow specific domains.

The TelemetryOS Solution: Proxy API

TelemetryOS provides a Proxy API that bypasses CORS restrictions by routing requests through the TelemetryOS platform.

How It Works

┌──────────────┐        ┌──────────────┐        ┌──────────────┐
│   Your App   │  (1)   │  TelemetryOS │  (2)   │  External    │
│   (Browser)  │───────▶│   Platform   │───────▶│     API      │
│              │        │  (Server)    │        │              │
└──────────────┘        └──────────────┘        └──────────────┘
                            │                         │
                            │◀────────────────────────┘
                            │         (3)
                            │
                            ▼
                        ┌──────────────┐
                        │   Your App   │
                        │  (Response)  │
                        └──────────────┘

Steps:

  1. Your app calls proxy().fetch(url) - Request goes to TelemetryOS platform
  2. Platform makes server-to-server request (no CORS restrictions)
  3. Platform returns response to your app

Key Point: Server-to-server requests don't have CORS restrictions. The Proxy API moves the request to the server side.

Using the Proxy API

Basic Usage

import { proxy } from '@telemetryos/sdk';

// Instead of direct fetch
// const response = await fetch('https://api.example.com/data');

// Use proxy
const response = await proxy().fetch('https://api.example.com/data');
const data = await response.json();

With API Keys

async function fetchWeather(city: string) {
  const apiKey = 'your-api-key-here';
  const url = `https://api.weatherapi.com/v1/current.json?key=${apiKey}&q=${city}`;

  // Proxy handles CORS
  const response = await proxy().fetch(url);

  if (!response.ok) {
    throw new Error(`HTTP ${response.status}: ${response.statusText}`);
  }

  return await response.json();
}

POST Requests

async function submitData(data: any) {
  const response = await proxy().fetch('https://api.example.com/submit', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json'
    },
    body: JSON.stringify(data)
  });

  return await response.json();
}

Custom Headers

const response = await proxy().fetch('https://api.example.com/data', {
  headers: {
    'Authorization': 'Bearer your-token',
    'X-Custom-Header': 'value'
  }
});

When Proxy API is NOT Needed

1. CORS-Enabled APIs

Some APIs explicitly enable CORS:

// This works because API allows all origins
const response = await fetch('https://api.coinbase.com/v2/exchange-rates');

How to Check: Look for API documentation mentioning CORS support or "browser-friendly" APIs.

2. TelemetryOS Resources

TelemetryOS-hosted resources already allow your origin:

// Media API returns publicUrls that work directly
const media = await media().getAllByTag('featured');
const imageUrl = media[0].publicUrls[0];

// Direct usage works
<img src={imageUrl} />

3. Public CDNs

Most CDNs enable CORS for asset delivery:

// Public CDN resources typically work
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/lib.js"></script>
<img src="https://images.unsplash.com/photo-123" />

Best Practices

1. Default to Proxy for External APIs

// ✅ Safe - always works
const response = await proxy().fetch('https://api.example.com/data');

// ❌ Risky - may work in dev, fail in production
const response = await fetch('https://api.example.com/data');

2. Test with Real APIs During Development

Local development may not enforce CORS the same way production does:

# Test with actual external APIs
tos serve

# Make real API calls, don't mock everything

3. Handle Proxy Errors

try {
  const response = await proxy().fetch(url);

  if (!response.ok) {
    throw new Error(`HTTP ${response.status}`);
  }

  const data = await response.json();
  return data;
} catch (error) {
  console.error('API call failed:', error);
  // Provide fallback or user-friendly error
  return null;
}

4. Cache Responses When Possible

The proxy benefits from platform-level caching, but you can add application-level caching:

async function fetchWithCache(url: string) {
  // Check device storage cache
  const cached = await store().device.get(`cache:${url}`);
  if (cached) return cached;

  // Fetch through proxy
  const response = await proxy().fetch(url);
  const data = await response.json();

  // Cache for next time
  await store().device.set(`cache:${url}`, data);

  return data;
}

Common CORS Scenarios

Scenario 1: Weather API Integration

// ❌ Without proxy - CORS error
async function getWeatherBad(city: string) {
  const response = await fetch(
    `https://api.weatherapi.com/v1/current.json?key=API_KEY&q=${city}`
  );
  return await response.json();
}

// ✅ With proxy - works perfectly
async function getWeatherGood(city: string) {
  const response = await proxy().fetch(
    `https://api.weatherapi.com/v1/current.json?key=API_KEY&q=${city}`
  );
  return await response.json();
}

Scenario 2: Your Own Backend API

// If you have a backend API
// ❌ Without CORS configuration or proxy
const response = await fetch('https://your-backend.com/api/data');

// ✅ Option 1: Configure CORS on your backend
// Allow: https://*.telemetryos.com

// ✅ Option 2: Use proxy
const response = await proxy().fetch('https://your-backend.com/api/data');

Scenario 3: GraphQL APIs

async function queryGraphQL(query: string) {
  const response = await proxy().fetch('https://api.example.com/graphql', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({ query })
  });

  return await response.json();
}

Troubleshooting CORS Issues

Error: "blocked by CORS policy"

Solution: Use proxy().fetch() instead of fetch()

Error: "Failed to fetch"

Check:

  1. Is the URL correct?
  2. Is the API endpoint online?
  3. Are you using proxy().fetch() for external APIs?
  4. Check browser console for specific CORS error

Local Development Works, Production Fails

Cause: Local development may not enforce CORS the same way.

Solution: Always test with proxy().fetch() even in local development.

API Returns 403/401

Not CORS: This is an authentication/authorization error.

Solution: Check API keys, tokens, and authentication headers.

Additional Proxy Benefits

Beyond solving CORS, the Proxy API provides:

  1. Caching - Platform caches responses, reducing API calls
  2. Bandwidth Optimization - Shared quota across all devices
  3. Monitoring - Track API usage and performance
  4. Rate Limiting - Platform handles rate limits gracefully
  5. Error Recovery - Automatic retries for transient failures

Summary

Key Takeaways:

  • ✅ TelemetryOS apps run in browser contexts with CORS enforcement
  • ✅ Use proxy().fetch() for all external API calls
  • ✅ Proxy API solves CORS + adds caching and optimization
  • ✅ Test with real APIs during development
  • ❌ Cannot disable CORS in browser environments
  • ❌ Direct fetch to non-CORS APIs will fail

Quick Decision Guide:

// External APIs (weather, news, social media, etc.)
await proxy().fetch(url) // ✅ Always use proxy

// TelemetryOS resources (media, etc.)
await fetch(publicUrl) // ✅ Direct fetch is fine

// Your own backend
// Either enable CORS on backend OR use proxy
await proxy().fetch(url) // ✅ Safest option

Next Steps


What’s Next