---
title: Developer Guide
description: "#### Overview **Flumotion Smart Multi CDN Unified Edge Compute** allows developers to write edge logic **once** using a unified API and deploy it automatically across multiple edge platforms, including: - Cloudflare Workers - CloudFront Lambda@Edge - Fastly Compute The Unified…"
url: "https://www.flumotion.com/docs/multiCDN/developer-guide/"
updated: "2026-09-04"
category: Guides / Edge Compute
---

# Developer Guide

#### Overview

**Flumotion Smart Multi CDN Unified Edge Compute** allows developers to write edge logic **once** using a unified API and deploy it automatically across multiple edge platforms, including:

- Cloudflare Workers
- CloudFront Lambda@Edge
- Fastly Compute

The Unified Edge Compute runtime abstracts the differences between these providers, allowing your code to remain platform-agnostic and portable.

Developers can implement two optional lifecycle handlers:

- `onRequest(request)` – Executed when a request arrives at the edge.
- `onResponse(request, response)` – Executed before the response is sent to the client.

#### Execution Model

##### onRequest(request)

Triggered when the incoming HTTP request reaches the edge.

Use it to:

- Modify headers
- Rewrite URLs
- Route to different backends
- Call external services
- Generate custom responses

##### Return value:

- Can return either `IORRequest` or `IORResponse`.
- Can return the result of `ior_fetch()`.

##### onResponse(request, response)

Triggered after a response is generated but before it is returned to the end user.

Use it to:

- Modify response headers
- Adjust status codes
- Rewrite the response body
- Add security headers

##### Return value:

- Must return an `IORResponse`.

#### Core APIs

##### IORRequest

Represents the incoming HTTP request.

##### Properties

| **Property** | **Type** | **Description** |
| --- | --- | --- |
| request.url | string | Full request URL |
| request.method | string | HTTP method (GET, POST, etc.) |
| request.headers | Headers | Request headers object |

##### Accessing Body

Use either `request.json()` or `request.text()` to retrieve the request body.

Note that these methods consume the body. If you only need to read the body and preserve it, clone the request and consume the body from the cloned instance.

##### Accessing Headers

`request.headers` behaves like the standard Web Fetch API `Headers`.

##### Get a Header

```
let userAgent = request.headers.get("User-Agent");
```

##### Set a Header

```
request.headers.set("x-custom-header", "value");
```

##### Delete a Header

```
request.headers.delete("Cookie");
```

##### Check if a Header Exists

```
request.headers.has("Authorization");
```

#### IORResponse

Represents an HTTP response.

An `IORResponse` can be:

- Returned from `ior_fetch()`.
- Manually constructed in `onRequest` or `onResponse`.
- Modified inside `onResponse`.

##### Properties

| **Property** | **Type** | **Description** |
| --- | --- | --- |
| response.status | number | HTTP status code |
| response.headers | Headers | Response headers |

##### Accessing Body

Use either `response.json()` or `response.text()` to retrieve the response body.

Note that these methods consume the body. If you only need to read the body and preserve it, you should clone it and consume the body from the cloned instance.

**This is not supported for CloudFront.**

##### Modifying Response Headers

##### Set a Header

```
response.headers.set("x-powered-by", "IO River");
```

##### Get a Header

```
let contentType = response.headers.get("Content-Type");
```

##### Delete a Header

```
response.headers.delete("Server");
```

##### Making Outbound Requests – `ior_fetch()`

`ior_fetch()` is the platform-agnostic fetch function. It works similarly to the standard Fetch API but is adapted internally for each edge provider.

##### Syntax

```
let response = await ior_fetch(url, {
    method: "POST",
    headers: headers,
    body: body,
});
```

##### Parameters

| **Parameter** | **Description** |
| --- | --- |
| url | String or URL object |
| method | HTTP method |
| headers | Headers object |
| body | Optional request body |

Returns:

Promise<IORResponse>

##### Util Library

The `ior_utils` library provides a unified set of cryptographic primitives designed to run consistently across all supported CDN providers within the Unified Edge Compute environment.

| **Function** | **Definition** | **Comments** |
| --- | --- | --- |
| sha256HashHex | sha256HashHex(messageString) | Returns a SHA-256 digest as a hex string |
| hmacSHA256 | hmacSHA256(key, message) | Returns a raw SHA-256 HMAC buffer for key + message |
| hmacSHA256Base64Url | hmacSHA256Base64Url(key, message) | Computes the HMAC and encodes it as Base64URL |
| base64UrlDecode | base64UrlDecode(str) | Converts a Base64URL string into a Uint8Array of bytes |
| bufferToString | bufferToString(buf) | Decodes a byte buffer into a UTF-8 text string |

**Note:** You do not need to import these functions. They are automatically available within your Edge Compute functions through the `ior_utils` namespace.

#### Examples

##### Example: Proxy Request to Another Host

```
async function onRequest(request) {
    var url = new URL(request.url);
    var headers = new Headers(request.headers);

    url.hostname = "test.example.com";
    headers.set("Authorization", "Basic abcd1234");

    let response = await ior_fetch(url, {
        headers: headers,
        method: request.method,
    });

    return response;
}
```

What This Does

- Rewrites the hostname.
- Adds an Authorization header.
- Forwards the original HTTP method.
- Returns the Origin response.

##### Example: Modify Response Before Sending to the End User

```
async function onResponse(request, response) {
    response.headers.set("foo", "bar");
    return response;
}
```

##### Example: Working With URL

You can use the standard Web API `URL`:

```
var url = new URL(request.url);

url.pathname = "/new-path";
url.searchParams.set("debug", "true");
```

##### Example: Creating a Custom Response

You can return a new response directly:

```
return new IORResponse("Blocked", {
    status: 403,
    headers: {
        "content-type": "text/plain",
    },
});
```

##### Example: Validating a JWT in a Request

```
async function jwt_decode(token, key, noVerify, algorithm) {
    // Check token
    if (!token) {
        throw new Error('No token supplied');
    }

    // Check segments
    const segments = token.split('.');
    if (segments.length !== 3) {
        throw new Error('Not enough or too many segments');
    }

    // All segments should be Base64
    const headerSeg = segments[0];
    const payloadSeg = segments[1];
    const signatureSeg = segments[2];

    // Base64 decode and parse JSON
    let payloadStr = await ior_utils.bufferToString(
        await ior_utils.base64UrlDecode(payloadSeg)
    );

    const payload = JSON.parse(payloadStr);

    if (!noVerify) {
        // Verify signature. _sign will return a Base64 string.
        const signingInput = [headerSeg, payloadSeg].join('.');

        if (!await _verify(signingInput, key, signatureSeg)) {
            throw new Error('Signature verification failed');
        }

        // Support for nbf and exp claims.
        // According to the RFC, they should be in seconds.
        if (payload.nbf && Date.now() < payload.nbf * 1000) {
            throw new Error('Token not yet active');
        }

        if (payload.exp && Date.now() > payload.exp * 1000) {
            throw new Error('Token expired');
        }
    }

    return payload;
}

function _constantTimeEquals(a, b) {
    if (a.length != b.length) {
        return false;
    }

    let xor = 0;

    for (let i = 0; i < a.length; i++) {
        xor |= (a.charCodeAt(i) ^ b.charCodeAt(i));
    }

    return 0 === xor;
}

async function _verify(input, key, signature) {
    let expectedSignature = await _sign(input, key);
    return _constantTimeEquals(signature, expectedSignature);
}

async function _sign(input, key) {
    return await ior_utils.hmacSHA256Base64Url(key, input);
}

async function onRequest(request) {
    // Update with your own key.
    const secret_key = await getSecret();

    if (!secret_key) {
        return new IORResponse(null, {
            status: 401,
            statusText: "Unauthirized"
        });
    }

    const jwtToken = request.urlObj.searchParams.get('jwt');

    // If no JWT token, then generate HTTP 401 response.
    if (!jwtToken) {
        return new IORResponse(null, {
            status: 401,
            statusText: "Unauthirized"
        });
    }

    try {
        await jwt_decode(jwtToken, secret_key);
    } catch (e) {
        console.log(e);

        return new IORResponse(null, {
            status: 401,
            statusText: "Unauthirized"
        });
    }

    // Remove the JWT from the query string if valid and return.
    let currentURL = request.urlObj;
    currentURL.searchParams.delete('jwt');

    return new IORRequest(
        new Request(currentURL, requestCtx.request)
    );
}

async function getSecret() {
    return "a-string-secret-at-least-256-bits-long";
}
```

#### Best Practices

##### 1. Clone Headers Before Modifying

Always create a new `Headers` object when forwarding:

```
var headers = new Headers(request.headers);
```

Avoid mutating the original unless necessary.

##### 2. Preserve Method and Body for Proxying

```
let response = await ior_fetch(url, {
    method: request.method,
    headers: headers,
    body: request.text(),
});
```

##### 3. Avoid Blocking Operations

All handlers must be asynchronous and non-blocking.

#### Error Handling

Use `try/catch` inside handlers:

```
async function onRequest(request) {
    try {
        return await ior_fetch(request.url);
    } catch (e) {
        return new IORResponse("Upstream Error", { status: 502 });
    }
}
```

#### Request Lifecycle

```
Incoming Request
      ↓
onRequest()
      ↓
(optional ior_fetch)
      ↓
Origin Response
      ↓
onResponse()
      ↓
End User
```

#### Conclusion

**Flumotion Smart Multi CDN Unified Edge Compute** enables developers to:

- Write once and deploy across multiple edge platforms.
- Use a standard Web API-like interface.
- Maintain full request and response control.
- Use a unified abstraction layer across major edge providers.
