mion uses @mionkit/run-types for automatic serialization.
Dates, BigInts, Sets, Maps, and other complex types are automatically serialized and deserialized without any extra configuration.
import {Routes, route} from '@mionkit/router';
import {memoryStoreService} from './full-example.app.ts';
// Your TypeScript types ARE the validation schema
interface User {
id: string;
email: string;
age: number;
birthDate: Date;
tags: Set<string>;
}
type NewUser = Omit<User, 'id'>;
// mion automatically:
// 1. Restores Date and Set from JSON
// 2. Validates user parameter
const routes = {
createUser: route((ctx, user: NewUser): User => {
// user is already validated and types are restored
console.log(user.birthDate instanceof Date); // true
console.log(user.tags instanceof Set); // true
return memoryStoreService.createUser(user);
}),
} satisfies Routes;
mion supports three serialization modes, each with different trade-offs for performance, data integrity, and use cases.
'json')The default JSON mode is optimized for speed. It only modifies properties that are not natively JSON-compatible (like Date, BigInt, Map, Set).
import {initRouter} from '@mionkit/router';
const router = initRouter({
serialize: 'json', // default
});
Characteristics:
Best for:
'stringifyJson')This mode creates a clean JSON output without mutating the original objects.
import {initRouter} from '@mionkit/router';
const router = initRouter({
serialize: 'stringifyJson',
});
Characteristics:
Best for:
'binary')Binary serialization provides the most compact data transfer, especially effective for numeric data.
import {initRouter} from '@mionkit/router';
const router = initRouter({
serialize: 'binary',
});
Characteristics:
NumInt32, NumUInt8, NumFloat) enables fixed-size encodingsBest for:
| Feature | json | stringifyJson | binary |
|---|---|---|---|
| Speed | Fastest | Fast | Fast |
| Payload size | Larger | Larger | Smallest |
| Mutates objects | Yes | No | No |
| Strips unknown props | No | Yes | Yes |
| Best for | Performance | Clean APIs | Bandwidth |
You might wonder why mion uses its own binary protocol instead of established formats like Protocol Buffers, MessagePack, or CBOR. The answer lies in full TypeScript support and flexibility.
Full TypeScript Feature Support
Unlike Protocol Buffers which requires separate .proto schema files and has limited type support, mion's binary format is generated directly from your TypeScript types. This means it supports:
string | number | MyTypeTypeA & TypeB[string, number, boolean]{ name?: string }(...args: string[]) => voidEnd-to-End Control
Since mion controls both the client and server serialization, we can optimize the protocol for our specific use case:
For applications where data transfer efficiency is critical, binary serialization provides more compact data transfer compared to JSON.
@mionkit/type-formats (like NumInt32, NumUInt8, NumFloat) allows the binary serializer to use optimized fixed-size encodings, significantly reducing payload sizes compared to JSON.import {PublicApi, Routes, initMionRouter, route} from '@mionkit/router';
import {NumInt32, NumUInt8, NumUInt16, NumFloat} from '@mionkit/type-formats/FormatsNumber';
/** Sensor reading with optimized numeric types for binary serialization */
export type SensorReading = {
sensorId: NumUInt16; // 0-65535, uses 2 bytes in binary
timestamp: NumInt32; // Unix timestamp, uses 4 bytes
temperature: NumFloat; // Temperature reading
humidity: NumUInt8; // 0-255%, uses 1 byte
pressure: NumFloat; // Atmospheric pressure
};
/** Batch of sensor readings for efficient transfer */
export type SensorBatch = {
batchId: NumUInt16;
readings: SensorReading[];
};
/** Statistics result with numeric data */
export type SensorStats = {
avgTemperature: NumFloat;
avgHumidity: NumUInt8;
avgPressure: NumFloat;
readingCount: NumUInt16;
};
// Define routes with binary serialization
const routes = {
/** Submit a single sensor reading */
submitReading: route((_ctx, reading: SensorReading): {success: boolean; id: NumUInt16} => ({
success: true,
id: reading.sensorId,
})),
/** Submit a batch of sensor readings for efficient transfer */
submitBatch: route((_ctx, batch: SensorBatch): {processed: NumUInt16} => ({
processed: batch.readings.length as NumUInt16,
})),
/** Get statistics from sensor readings */
getStats: route(
(_ctx, sensorId: NumUInt16): SensorStats => ({
avgTemperature: 22.5 as NumFloat,
avgHumidity: 65 as NumUInt8,
avgPressure: 1013.25 as NumFloat,
readingCount: 100 as NumUInt16,
})
),
} satisfies Routes;
// Initialize router with binary serialization globally
initMionRouter(routes, {serializer: 'binary'});
// Export API type for client usage
export type BinaryApi = PublicApi<typeof routes>;
You can also enable binary serialization for specific routes by setting the serializer option:
const routes = {
// JSON serialization (default)
jsonRoute: route((ctx, data: MyType) => data),
// Binary serialization for this route
binaryRoute: route((ctx, data: MyType) => data, {serializer: 'binary'}),
} satisfies Routes;
The client automatically detects the serialization format and handles it transparently:
import {initClient} from '@mionkit/client';
import type {BinaryApi, SensorReading, SensorBatch} from './binary-server-example.ts';
import type {NumUInt16, NumUInt8, NumFloat, NumInt32} from '@mionkit/type-formats/FormatsNumber';
// Initialize client with the server URL
const {routes} = initClient<BinaryApi>({baseURL: 'http://localhost:3000'});
// Create a sensor reading with optimized numeric types
const reading: SensorReading = {
sensorId: 1 as NumUInt16,
timestamp: Math.floor(Date.now() / 1000) as NumInt32,
temperature: 23.5 as NumFloat,
humidity: 65 as NumUInt8,
pressure: 1013.25 as NumFloat,
};
// Submit a single reading
const [result, error] = await routes.submitReading(reading).call();
console.log(result, error);
// Logs { success: true, id: 1 }
// Submit a batch of readings for efficient transfer
const batch: SensorBatch = {
batchId: 1 as NumUInt16,
readings: [reading, {...reading, sensorId: 2 as NumUInt16}],
};
const [batchResult] = await routes.submitBatch(batch).call();
console.log(batchResult);
// Logs { processed: 2 }
// Get statistics for a sensor
const [stats] = await routes.getStats(1 as NumUInt16).call();
console.log(stats);
// Logs { avgTemperature: 22.5, avgHumidity: 65, avgPressure: 1013.25, readingCount: 100 }