Platforms

Cloudflare Workers

Running mion APIs on Cloudflare Workers using the Web standard fetch handler.

mion can run on Cloudflare Workers for globally-distributed edge deployments. The @mionjs/platform-cloudflare package provides a Web standard Request -> Response fetch handler with access to Worker bindings via the platform context.

Installation

npm install @mionjs/platform-cloudflare @mionjs/router

Quick Start

import {initMionRouter} from '@mionjs/router';
import {createCloudflareHandler} from '@mionjs/platform-cloudflare';
import {routes} from './cloudflare-routes.ts';

await initMionRouter(routes, {aot: true});

export default createCloudflareHandler();

wrangler.toml Example

A minimal wrangler.toml for a mion Worker:

name = "my-mion-api"
main = "dist/worker.js"
compatibility_date = "2024-01-01"

The main field points to the bundled output produced by Vite (see AOT Compilation Required below). Run wrangler deploy to publish.

Configuration

You can pass configuration options to createCloudflareHandler:

import {initMionRouter} from '@mionjs/router';
import {createCloudflareHandler} from '@mionjs/platform-cloudflare';
import {routes} from './cloudflare-routes.ts';

await initMionRouter(routes, {aot: true, basePath: 'api'});

export default createCloudflareHandler({
    basePath: '/api',
    defaultResponseHeaders: {'access-control-allow-origin': '*'},
});
The Cloudflare handler exposes env (your Worker bindings — KV, D1, R2, secrets, etc.) and ctx (the Worker ExecutionContext with waitUntil/passThroughOnException) on the mion call context's platform context. This is the canonical way to access Worker bindings from your route handlers.

AOT Compilation Required

Cloudflare Workers do not allow new Function() or eval(). You must enable mion's AOT compilation in your Vite build, otherwise the router will throw AOTCacheError when it tries to register routes inside the Worker.

mion's default JIT compilation generates validation and serialization functions at runtime using new Function(). Workers block this for security reasons, so Cloudflare projects must be bundled with pre-compiled AOT caches. The mion Vite plugin handles this automatically — see AOT Compilation for the full explanation.

Vite Configuration

A minimal vite.config.ts for a Cloudflare Worker:

vite.config.ts
import {defineConfig} from 'vite';
import {mionPlugin} from '@mionjs/devtools/vite-plugin';

export default defineConfig({
    plugins: [
        mionPlugin({
            aotCaches: {},
            server: {
                startScript: 'src/index.ts',
                runMode: 'buildOnly',
            },
        }),
    ],
    build: {
        lib: {
            entry: 'src/index.ts',
            formats: ['es'],
            fileName: 'worker',
        },
        rollupOptions: {
            output: {inlineDynamicImports: true},
        },
    },
});

inlineDynamicImports: true is required because Workers don't support dynamic code splitting — every dependency must be bundled into a single file.

Worker Entry

The Worker entry file calls initMionRouter with aot: true to enable strict AOT mode. In strict mode @mionjs/run-types is not loaded at all, every route must be present in the AOT cache, and the Vite plugin auto-injects the cache contents at bundle time:

import {initMionRouter} from '@mionjs/router';
import {createCloudflareHandler} from '@mionjs/platform-cloudflare';
import {routes} from './cloudflare-routes.ts';

await initMionRouter(routes, {aot: true});

export default createCloudflareHandler();
You don't need to import virtual:mion-aot/caches or call addAOTCaches() manually — when the plugin's aotCaches option is set, the Vite plugin auto-wires everything. For a deeper explanation of how this works see AOT Compilation and Vite Configuration.

For TypeScript support of the virtual modules (only needed if you import them manually), add this to your tsconfig.json:

tsconfig.json
{
  "compilerOptions": {
    "types": ["@mionjs/devtools/virtual-modules"]
  }
}

Type Reference

CloudflareHandlerOptions

export interface CloudflareHandlerOptions {
    /** Set of default response headers to add to every response */
    defaultResponseHeaders: Record<string, string>;
    /** Path prefix to strip from incoming URL (e.g., '/api/mion') */
    basePath: string;
}