Hidden page. It waits for review: the sidebar, search and search engines leave it out, and listed pages don't link to it.
Validating on the client
experimental_validationLink checks inputs before sending and results when they arrive, against a contract.
On this page
Clients ship no schemas by default. To check calls against a contract in the browser, add experimental_validationLink: inputs are validated before anything is sent (the same BAD_REQUEST the server returns, so forms can show its issues), and results and subscription events are checked when they arrive (BAD_RESPONSE when the server drifted from the contract, with the issues on its cause):
const const client: TrpcClient<{
post: {
byId: ContractProcedure<"query", ContractWith<ContractWith<ContractRootDef<{}>, {
inputIn: {
id: string;
};
inputOut: {
id: string;
};
errors: SchemaValidationError<z.ZodObject<{
id: z.ZodString;
}, z.core.$strip>>;
requires: never;
}>, {
outputIn: {
id: string;
title: string;
author: string | null;
createdAt: Date;
};
outputOut: {
id: string;
title: string;
author: string | null;
createdAt: Date;
};
requires: never;
}>>;
};
}, {}, undefined, LinksTypes<readonly [...]>>
client = createTrpcClient<{
post: {
byId: ContractProcedure<"query", ContractWith<ContractWith<ContractRootDef<{}>, {
inputIn: {
id: string;
};
inputOut: {
id: string;
};
errors: SchemaValidationError<z.ZodObject<{
id: z.ZodString;
}, z.core.$strip>>;
requires: never;
}>, {
outputIn: {
id: string;
title: string;
author: string | null;
createdAt: Date;
};
outputOut: {
id: string;
title: string;
author: string | null;
createdAt: Date;
};
requires: never;
}>>;
};
}, readonly [TrpcLink<{}, any>, TrpcLink<...>]>(opts: TypedClientOptions<...>): TrpcClient<...>
createTrpcClient({
TypedClientOptions<{ post: { byId: ContractProcedure<"query", ContractWith<ContractWith<ContractRootDef<{}>, { inputIn: { id: string; }; inputOut: { id: string; }; errors: SchemaValidationError<ZodObject<{ id: ZodString; }, $strip>>; requires: never; }>, { ...; }>>; }; }, readonly [...]>.router: {
post: {
byId: ContractProcedure<"query", ContractWith<ContractWith<ContractRootDef<{}>, {
inputIn: {
id: string;
};
inputOut: {
id: string;
};
errors: SchemaValidationError<z.ZodObject<{
id: z.ZodString;
}, z.core.$strip>>;
requires: never;
}>, {
outputIn: {
id: string;
title: string;
author: string | null;
createdAt: Date;
};
outputOut: {
id: string;
title: string;
author: string | null;
createdAt: Date;
};
requires: never;
}>>;
};
}
router: const appContract: {
post: {
byId: ContractProcedure<"query", ContractWith<ContractWith<ContractRootDef<{}>, {
inputIn: {
id: string;
};
inputOut: {
id: string;
};
errors: SchemaValidationError<z.ZodObject<{
id: z.ZodString;
}, z.core.$strip>>;
requires: never;
}>, {
outputIn: {
id: string;
title: string;
author: string | null;
createdAt: Date;
};
outputOut: {
id: string;
title: string;
author: string | null;
createdAt: Date;
};
requires: never;
}>>;
};
}
appContract,
TypedClientOptions<{ post: { byId: ContractProcedure<"query", ContractWith<ContractWith<ContractRootDef<{}>, { inputIn: { id: string; }; inputOut: { id: string; }; errors: SchemaValidationError<ZodObject<{ id: ZodString; }, $strip>>; requires: never; }>, { ...; }>>; }; }, readonly [...]>.links: readonly [TrpcLink<{}, any>, TrpcLink<{
form: JsonForm;
provides: "response";
}, any>]
links: [
function experimental_validationLink(opts: ValidationLinkOptions): TrpcLink (+1 overload)experimental_validationLink({ ValidationLinkOptions.contract: objectcontract: const appContract: {
post: {
byId: ContractProcedure<"query", ContractWith<ContractWith<ContractRootDef<{}>, {
inputIn: {
id: string;
};
inputOut: {
id: string;
};
errors: SchemaValidationError<z.ZodObject<{
id: z.ZodString;
}, z.core.$strip>>;
requires: never;
}>, {
outputIn: {
id: string;
title: string;
author: string | null;
createdAt: Date;
};
outputOut: {
id: string;
title: string;
author: string | null;
createdAt: Date;
};
requires: never;
}>>;
};
}
appContract }),
httpLink<{
url: string;
}>(opts: {
url: string;
}): TrpcLink<{
form: JsonForm;
provides: "response";
}, any>
httpLink({ url: stringurl }),
],
});Results are checked against the output's type side, so Effect Schema transformations don't run a second time. With experimental_openApiLink, whose results are plain JSON, output: 'json' decodes them with the Effect Schema's JSON codec, so Dates, bigints and classes come back as their types. Standard Schema outputs are validated as they are.
See Contract-first for writing the contract, and Calling REST from the client for experimental_openApiLink.
Credits
experimental_validationLink follows oRPC's request validation and response validation plugins.