Server

Validation

Automatic validation in mion using RunTypes.

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;
When a type has an index signature (e.g. [key: string]: any), strictTypes is automatically skipped since the type explicitly allows arbitrary properties.