Client
Modern client for mion Apis:
- Strongly typed apis with autocompletion ans static type checking.
- Fully typed list of remote methods with it's parameters and return values.
- Automattic Validation and Serialization out of the box.
- Local Validation (no need to make a server request to validate parameters)
- Prefill request data to persist across multiple calls.
End to End Type Safety

Adding Client Support in the Server
The client needs a couple of special routes to get the metadata required for validation and serialization so we need to add them to the API. We can register these Routes separately from our application routes.
It is also required to export the RemoteApi
type returned from registerRoutes
so it can be used by the client.
import {Routes, registerRoutes} from '@mionkit/router';
import {clientRoutes} from '@mionkit/common';
import {initAwsLambdaRouter, awsLambdaHandler} from '@mionkit/serverless';
const routes = {
// ... my Application Routes
} satisfies Routes;
// init & register routes
initAwsLambdaRouter();
const myApi = registerRoutes(routes);
// register client routes
// these routes serve metadata required for validation and serialization on the client
registerRoutes(clientRoutes);
// Export Routes type (to be used by the client)
export type MyApi = typeof myApi;
// export aws lambda handler
export const handler = awsLambdaHandler;
Using the client
To use mion client we just need to initialize the client using the RemoteApi
type returned from registerRoutes
.
It is important to just import the type so we don't import any of the actual routes or backend code into the client.
import {initClient} from '@mionkit/client';
// importing only the RemoteApi type from server
import type {MyApi} from './server.routes';
import {ParamsValidationResponse} from '@mionkit/reflection';
const port = 8076;
const baseURL = `http://localhost:${port}`;
const {routes, hooks} = initClient<MyApi>({baseURL});
Calling remote routes
The methods
object returned from initClient
contains all remote routes and hooks.
If a hook is not public (does not expect any parameters and cant return data) then it is not included within the methods
object.
Remote routes and hooks are strongly types and contains all the same parameters of routes and methods in the API except the first CallContext parameter.
Calling routes and hooks in the client generates a RouteSubRequest
or HookSubRequest
. This is so multiple HookSubRequest
can be added to a single RouteSubRequest
.
We use the call()
method from RouteSubRequest
to perform the remote call, you can pass any required HookSubRequest
data to the this call method.
// calls sumTwo route in the server
const authSubRequest = hooks.auth('myToken-XYZ');
const sumTwoResp = await routes.utils.sum(5, 2).call(authSubRequest);
console.log(sumTwoResp); // 7
Prefilling Hooks data
For cases like authorization hooks that are required for all request we can use the prefill()
method of HookSubRequest
. This will automatically persist the subRequest in local storage and prefill any future route calls that requires that hook.
// prefills the token for any future requests, value is stored in localStorage
await hooks.auth('myToken-XYZ').prefill();
// // calls sumTwo route in the server
const sumTwoResponse = await routes.utils.sum(5, 2).call();
console.log(sumTwoResponse); // 7
Full example
import {initClient} from '@mionkit/client';
// importing only the RemoteApi type from server
import type {MyApi} from './server.routes';
import {ParamsValidationResponse} from '@mionkit/reflection';
const port = 8076;
const baseURL = `http://localhost:${port}`;
const {routes, hooks} = initClient<MyApi>({baseURL});
// prefills the token for any future requests, value is stored in localStorage
await hooks.auth('myToken-XYZ').prefill();
// calls sayHello route in the server
const sayHello = await routes.utils.sayHello({id: '123', name: 'John', surname: 'Doe'}).call();
console.log(sayHello); // Hello John Doe
// calls sumTwo route in the server
const sumTwoResp = await routes.utils.sum(5, 2).call(hooks.auth('myToken-XYZ'));
console.log(sumTwoResp); // 7
// validate parameters locally without calling the server
const validationResp: ParamsValidationResponse = await routes.utils
.sayHello({id: '123', name: 'John', surname: 'Doe'})
.validate();
console.log(validationResp); // {hasErrors: false, totalErrors: 0, errors: []}
Type Reference
RemoteApi
This type could be considered your API schema to be used by the client. This is a Mapping of your public hooks an routes to RemoteMethodMetadata
export type RemoteApi<Type extends Routes> = // ... Maps Public Hooks and Routes to RemoteMethodMetadata
RemoteMethodMetadata
Required Metadata from Hook and Routes required for validation and serialization in the client.
export interface RemoteMethodMetadata<H extends Handler = any> {
/** Type reference to the route handler, it's runtime value is actually null, just used statically by typescript. */
_handler: RemoteHandler<H>;
/** Json serializable structure so the Type information can be transmitted over the wire */
serializedTypes: SerializedTypes;
isRoute: boolean;
id: string;
inHeader: boolean;
enableValidation: boolean;
enableSerialization: boolean;
params: string[];
hookIds?: string[];
pathPointers?: string[][];
headerName?: string;
}
SubRequest
export interface SubRequest<RM extends RemoteMethodMetadata> {
pointer: string[];
id: RM['id'];
isResolved: boolean;
params: Parameters<RM['_handler']>;
return?: HandlerSuccessResponse<RM>;
error?: HandlerFailResponse<RM>;
validationResponse?: ParamsValidationResponse;
serializedParams?: any[];
// note this type can't contain functions, so it can be stored/restored from localStorage
}
RouteSubRequest
export interface RouteSubRequest<RR extends RemoteRouteMetadata> extends SubRequest<RR> {
/**
* Validates Route's parameters. Throws RpcError if validation fails.
* @returns {hasErrors: false, totalErrors: 0, errors: []}
*/
validate: () => Promise<ParamsValidationResponse>;
/**
* Calls a remote route.
* Validates route and required hooks request parameters locally before calling the remote route.
* Throws RpcError if anything fails during the call (including validation or serialization) or if the remote route returns an error.
* @param hooks HookSubRequests requires by the route
* @returns
*/
call: <RHList extends HookSubRequest<any>[]>(...hooks: RHList) => Promise<HandlerSuccessResponse<RR>>;
}
HookSubRequest
export interface HookSubRequest<RH extends RemoteHookMetadata | RemoteHeaderHookMetadata> extends SubRequest<RH> {
/**
* Validates Hooks's parameters. Throws RpcError if validation fails.
* @returns {hasErrors: false, totalErrors: 0, errors: []}
*/
validate: () => Promise<ParamsValidationResponse>;
/**
* Prefills Hook's parameters for any future request. Parameters are also persisted in local storage for future requests.
* Validates and Serializes parameters before storing in local storage.
* Throws RpcError if validation or serialization fail or if the parameters can't be persisted.
* @returns Promise<void>
*/
prefill: () => Promise<void>;
/**
* Removes prefilled value.
* Throws RpcError if something fails removing the prefilled parameters
* @returns Promise<void>
*/
removePrefill: () => Promise<void>;
}