Validation
mion uses @mionjs/run-types for automatic validation.
This is a powerful type system that extracts TypeScript type metadata at compile time and uses it at runtime.
No need to manage schemas or write extra code for validation.
Automatic Validation
All routes and middleFns parameters are automatically validated before the method gets called.
If validation fails, an RpcError is thrown with details about the validation errors and method gets never called.
import {Routes, route} from '@mionjs/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;
If an invalid request is sent, a ValidationError (which extends RpcError) is thrown:
import {RpcError, RunTypeError, ValidationError} from '@mionjs/core';
// Example validation error thrown when invalid data is received
const validationError: ValidationError = new RpcError({
statusCode: 400,
type: 'validation-error',
publicMessage: "Invalid params in 'createUser', validation failed.",
errorData: {
typeErrors: [
{path: ['email'], expected: 'string'},
{path: ['age'], expected: 'number'},
] as RunTypeError[],
},
});
Validation Functions
mion provides two main validation functions with different use cases:
isType - Fast Boolean Check
isType is optimized for performance and returns a simple true or false result. Use this when you only need to know if data is valid without detailed error information.
import {createIsTypeFn} from '@mionjs/run-types';
interface User {
name: string;
age: number;
}
const isUser = await createIsTypeFn<User>();
isUser({name: 'John', age: 30}); // true
isUser({name: 'John'}); // false (missing age)
isUser({name: 'John', age: '30'}); // false (age is string)
When to use isType:
- Quick validation checks in hot paths
- Guard clauses where you just need a boolean
- Performance-critical code where detailed errors aren't needed
getTypeErrors - Detailed Error Information
getTypeErrors returns comprehensive error data including the path to invalid properties and expected types. Use this when you need to provide detailed feedback to users or for debugging.
import {createTypeErrorsFn} from '@mionjs/run-types';
interface User {
name: string;
age: number;
}
const getUserErrors = await createTypeErrorsFn<User>();
const errors = getUserErrors({name: 123, age: 'invalid'});
// Returns: [
// { path: ['name'], expected: 'string', actual: 'number' },
// { path: ['age'], expected: 'number', actual: 'string' }
// ]
When to use getTypeErrors:
- Form validation with user feedback
- API error responses with detailed messages
- Debugging and logging validation failures
- When you need to know exactly what went wrong
Strict Types
By default, mion allows objects with extra properties to pass validation. When strictTypes is enabled, objects with unknown or extra properties are rejected.
This is useful for security-sensitive endpoints where you want to ensure no unexpected data is passed.
Using strictTypes with run-types
import {createIsTypeFn, createTypeErrorsFn} from '@mionjs/run-types';
interface User {
name: string;
age: number;
}
// With strictTypes, extra properties are rejected
const isUser = await createIsTypeFn<User>({strictTypes: true});
isUser({name: 'John', age: 30}); // true
isUser({name: 'John', age: 30, extra: 'value'}); // false (unknown property 'extra')
// typeErrors also reports unknown properties
const getUserErrors = await createTypeErrorsFn<User>({strictTypes: true});
getUserErrors({name: 'John', age: 30}); // []
getUserErrors({name: 'John', age: 30, extra: 'value'});
// Returns: [{ path: ['extra'], expected: 'never' }]
Using strictTypes in the router
You can enable strictTypes globally via router options or per-route:
import {Routes, route, initRouter} from '@mionjs/router';
interface User {
name: string;
email: string;
age: number;
}
// Enable strictTypes globally: rejects objects with unknown/extra properties
await initRouter({strictTypes: true});
// Or enable strictTypes per-route
const routes = {
// this route rejects objects with extra properties
createUser: route((ctx, user: User): User => user, {strictTypes: true}),
// this route accepts objects with extra properties
updateUser: route((ctx, user: Partial<User>): Partial<User> => user, {strictTypes: false}),
} satisfies Routes;
[key: string]: any), strictTypes is automatically skipped since the type explicitly allows arbitrary properties.