mion's Vite plugin provides Ahead-of-Time (AOT) compilation that pre-compiles critical runtime functions at build time. Instead of writing files to disk, AOT caches are generated as Vite virtual modules that get embedded directly into your server and client bundles.
eval or new Functionaot: true is set, the heavy @mionkit/run-types package is NOT loaded at allmion uses Just-In-Time (JIT) compilation to generate validation and serialization functions at runtime. While this provides great flexibility, it has two main drawbacks:
AOT compilation solves both issues by pre-compiling these functions during your Vite build process.
The mion Vite plugin generates AOT caches through this pipeline:
buildStart — The plugin spawns a vite-node child process that runs your server start scriptThe plugin generates these virtual modules that you can import in your application:
| Module | Description |
|---|---|
virtual:mion-aot/jit-fns | Pre-compiled JIT validation/serialization functions |
virtual:mion-aot/pure-fns | Pre-compiled pure functions cache |
virtual:mion-aot/router-cache | Router methods metadata cache |
virtual:mion-aot/caches | Combined module — imports and registers all 3 caches |
virtual:mion-server-pure-fns | Server pure functions extracted from client code |
For TypeScript support, add the virtual module type definitions to your tsconfig.json:
{
"compilerOptions": {
"types": ["@mionkit/devtools/virtual-modules"]
}
}
The plugin caches AOT results on disk (in Vite's node_modules/.vite/ by default) so subsequent builds skip the expensive vite-node step when your server source hasn't changed. The cache is invalidated automatically when source files change, the devtools version updates, or AOT options change.
Set MION_AOT_FORCE=true to force regeneration regardless of the cache.
The AOT build process works by executing your start script and capturing all routes that get registered. This means:
import {Routes, route} from '@mionkit/router';
import {runType} from '@mionkit/run-types';
interface User {
id: string;
name: string;
}
interface AuditLog {
action: string;
timestamp: Date;
}
declare function findUser(id: string): User;
const routes = {
// ✅ User type WILL be compiled (exposed in return type)
getUser: route((ctx, id: string): User => {
return findUser(id);
}),
// ✅ string parameter WILL be compiled
saveUser: route((ctx, name: string): void => {
// ...
}),
} satisfies Routes;
// ❌ AuditLog type will NOT be compiled!
// It's not used in any route parameter or return type
async function logAction(log: AuditLog): Promise<void> {
const validate = await runType<AuditLog>().createJitFunction('isType');
// This will fail in secure environments (no JIT available)
}
// ❌ This route won't be compiled if it's in a file
// that is not imported by the start script
// routes-admin.ts (not imported)
const adminRoutes = {
deleteUser: route((ctx, id: string): void => {
/* ... */
}),
};
runType<T>() directly, make sure T is also used in at least one route parameter or return type.Import the combined caches module before initializing the router. The virtual:mion-aot/caches module automatically registers all caches:
// Import cache exports from your AOT package
import {routerCache, jitFnsCache, pureFnsCache} from 'my-api-aot';
// Import the cache loading function from @mionkit/core
import {addAOTCaches} from '@mionkit/core';
// Now initialize your router - it will use the pre-compiled functions
import {initMionRouter} from '@mionkit/router';
import {routes as myRoutes} from './aot-routes-example.ts';
// Load the pre-compiled caches BEFORE initializing the router
addAOTCaches(jitFnsCache, pureFnsCache);
export const myApi = await initMionRouter(myRoutes);
When you set aot: true in the router options, the router operates in strict AOT mode:
@mionkit/run-types package is NOT loaded at allAOTCacheError is thrown immediatelyawait initMionRouter(routes, {
aot: true, // Strict AOT mode - run-types will NOT be loaded
});
aot: true is set, ALL routes and middleFns MUST be present in the AOT cache. If any are missing, the router will throw an AOTCacheError instead of falling back to JIT compilation.If you still want to preload AOT caches (for offline support or reduced latency), load them before initializing the client:
// Load custom AOT caches (optional for client)
import {jitFnsCache, pureFnsCache} from 'my-api-aot';
import {addAOTCaches} from '@mionkit/core';
addAOTCaches(jitFnsCache, pureFnsCache);
// Then initialize the client
import {initClient} from '@mionkit/client';
import type {MyApi} from './aot-routes-example.ts';
const {routes, middleFns} = initClient<MyApi>({baseURL: 'http://localhost:3000'});
// Load AOT caches first
import {routerCache, jitFnsCache, pureFnsCache} from 'my-api-aot';
import {addAOTCaches} from '@mionkit/core';
// Then initialize router and server
import {initMionRouter} from '@mionkit/router';
import {initHttp} from '@mionkit/node';
import {routes} from './aot-routes-example.ts';
// Load the pre-compiled caches BEFORE initializing the router
addAOTCaches(jitFnsCache, pureFnsCache);
export const myApi = await initMionRouter(routes);
initHttp({port: 3000});
console.log('Server running on port 3000');
import {Routes, route} from '@mionkit/router';
export interface User {
id: string;
name: string;
email: string;
}
export type MyApi = typeof routes;
export const routes = {
users: {
getById: route((ctx, id: string): User => {
return {id, name: 'John', email: 'john@example.com'};
}),
create: route((ctx, user: Omit<User, 'id'>): User => {
return {id: 'USER-123', ...user};
}),
},
} satisfies Routes;
AOT compilation is essential for running mion in secure environments that restrict dynamic code execution. Environments like Cloudflare Workers, Deno Deploy, and some enterprise environments prohibit:
eval()new Function()mion's JIT compilation normally uses new Function() to generate validation and serialization functions. AOT compilation pre-generates these functions as static code, eliminating the need for runtime code generation.