ExperimentalContracts and OpenAPI may change in any release.
Calling REST from the client
experimental_openApiLink sends each call to its REST route, typed from a contract.
On this page
experimental_openApiLink sends each call to its REST route. It needs the routes, so it takes a contract; it never needs the router.
import { function experimental_toContract<TRouter extends object>(router: TRouter): ContractJson<TRouter>experimental_toContract } from '@trpc12/trpc/contract';
import { function experimental_openApiLink(opts: OpenApiLinkOptions): TrpcLink<{
form: JsonForm;
}>
experimental_openApiLink } from '@trpc12/trpc/openapi';
const const contract: ContractJson<{
post: {
byId: Procedure<{
type: "query";
ctx: Context;
meta: {
route?: Route | undefined;
};
input: {
id: string;
};
output: Post | undefined;
errors: SchemaValidationError<z.ZodObject<{
id: z.ZodString;
}, z.core.$strip>>;
services: never;
}>;
};
}>
contract = experimental_toContract<{
post: {
byId: Procedure<{
type: "query";
ctx: Context;
meta: {
route?: Route | undefined;
};
input: {
id: string;
};
output: Post | undefined;
errors: SchemaValidationError<z.ZodObject<{
id: z.ZodString;
}, z.core.$strip>>;
services: never;
}>;
};
}>(router: {
post: {
byId: Procedure<{
type: "query";
ctx: Context;
meta: {
route?: Route | undefined;
};
input: {
id: string;
};
output: Post | undefined;
errors: SchemaValidationError<z.ZodObject<{
id: z.ZodString;
}, z.core.$strip>>;
services: never;
}>;
};
}): ContractJson<...>
experimental_toContract(const appRouter: {
post: {
byId: Procedure<{
type: "query";
ctx: Context;
meta: {
route?: Route | undefined;
};
input: {
id: string;
};
output: Post | undefined;
errors: SchemaValidationError<z.ZodObject<{
id: z.ZodString;
}, z.core.$strip>>;
services: never;
}>;
};
}
appRouter); // or the JSON, shipped to the client
const const client: TrpcClient<{
post: {
byId: Procedure<{
type: "query";
ctx: {};
meta: {
route?: Route | undefined;
};
input: {
id: string;
};
output: Post | undefined;
errors: SchemaValidationError<z.ZodObject<{
id: z.ZodString;
}, z.core.$strip>>;
services: never;
}>;
};
}, {}, undefined, LinksTypes<readonly [TrpcLink<{
form: JsonForm;
}, any>]>>
client = createTrpcClient<ContractJson<{
post: {
byId: Procedure<{
type: "query";
ctx: Context;
meta: {
route?: Route | undefined;
};
input: {
id: string;
};
output: Post | undefined;
errors: SchemaValidationError<z.ZodObject<{
id: z.ZodString;
}, z.core.$strip>>;
services: never;
}>;
};
}>, readonly [TrpcLink<{
form: JsonForm;
}, any>]>(opts: TypedClientOptions<ContractJson<{
post: {
byId: Procedure<{
type: "query";
ctx: Context;
meta: {
route?: Route | undefined;
};
input: {
id: string;
};
output: Post | undefined;
errors: SchemaValidationError<z.ZodObject<{
id: z.ZodString;
}, z.core.$strip>>;
services: never;
}>;
};
}>, readonly [...]>): TrpcClient<...>
createTrpcClient({
TypedClientOptions<ContractJson<{ post: { byId: Procedure<{ type: "query"; ctx: Context; meta: { route?: Route | undefined; }; input: { id: string; }; output: Post | undefined; errors: SchemaValidationError<ZodObject<{ id: ZodString; }, $strip>>; services: never; }>; }; }>, readonly [...]>.router: ContractJson<{
post: {
byId: Procedure<{
type: "query";
ctx: Context;
meta: {
route?: Route | undefined;
};
input: {
id: string;
};
output: Post | undefined;
errors: SchemaValidationError<z.ZodObject<{
id: z.ZodString;
}, z.core.$strip>>;
services: never;
}>;
};
}>
router: const contract: ContractJson<{
post: {
byId: Procedure<{
type: "query";
ctx: Context;
meta: {
route?: Route | undefined;
};
input: {
id: string;
};
output: Post | undefined;
errors: SchemaValidationError<z.ZodObject<{
id: z.ZodString;
}, z.core.$strip>>;
services: never;
}>;
};
}>
contract,
TypedClientOptions<ContractJson<{ post: { byId: Procedure<{ type: "query"; ctx: Context; meta: { route?: Route | undefined; }; input: { id: string; }; output: Post | undefined; errors: SchemaValidationError<ZodObject<{ id: ZodString; }, $strip>>; services: never; }>; }; }>, readonly [...]>.links: readonly [TrpcLink<{
form: JsonForm;
}, any>]
links: [
function experimental_openApiLink(opts: OpenApiLinkOptions): TrpcLink<{
form: JsonForm;
}>
experimental_openApiLink({
OpenApiLinkOptions.url: string | URLurl: 'https://api.example.com',
OpenApiLinkOptions.contract: ContractJson<any>contract,
OpenApiLinkOptions.headers?: HeadersInit | ((opts: {
op: Operation;
}) => HeadersInit | Promise<HeadersInit>) | undefined
headers: { 'x-user': 'ada' },
}),
],
});
await const client: TrpcClient<{
post: {
byId: Procedure<{
type: "query";
ctx: {};
meta: {
route?: Route | undefined;
};
input: {
id: string;
};
output: Post | undefined;
errors: SchemaValidationError<z.ZodObject<{
id: z.ZodString;
}, z.core.$strip>>;
services: never;
}>;
};
}, {}, undefined, LinksTypes<readonly [TrpcLink<{
form: JsonForm;
}, any>]>>
client.post: ClientRouter<{
byId: Procedure<{
type: "query";
ctx: {};
meta: {
route?: Route | undefined;
};
input: {
id: string;
};
output: Post | undefined;
errors: SchemaValidationError<z.ZodObject<{
id: z.ZodString;
}, z.core.$strip>>;
services: never;
}>;
}, {}, undefined, LinksTypes<readonly [TrpcLink<{
form: JsonForm;
}, any>]>>
post.byId: {
query(input: {
id: string;
}, opts?: CallOptions<{}> | undefined): TrpcPromise<{
id: string;
title: string;
author: string | null;
createdAt: string;
} | undefined, CallError<{
type: "query";
ctx: {};
meta: {
route?: Route | undefined;
};
input: {
id: string;
};
output: Post | undefined;
errors: SchemaValidationError<z.ZodObject<{
id: z.ZodString;
}, z.core.$strip>>;
services: never;
}, JsonForm, never>>;
}
byId.function query(input: {
id: string;
}, opts?: CallOptions<{}> | undefined): TrpcPromise<{
id: string;
title: string;
author: string | null;
createdAt: string;
} | undefined, CallError<{
type: "query";
ctx: {};
meta: {
route?: Route | undefined;
};
input: {
id: string;
};
output: Post | undefined;
errors: SchemaValidationError<z.ZodObject<{
id: z.ZodString;
}, z.core.$strip>>;
services: never;
}, JsonForm, never>>
query({ id: stringid: '1' }); // GET https://api.example.com/api/posts/1Declared errors come back as typed TrpcErrors, as they do over RPC.
REST responses are plain JSON, and the client types say so: a createdAt: Date output is a string through experimental_openApiLink. To get the types back, put experimental_validationLink({ contract, output: 'json' }) in front: it decodes each result with the Effect Schema's JSON codec, and the client types results as the schemas' types again (Validating on the client):
links: [
experimental_validationLink({ contract: appContract, output: 'json' }),
experimental_openApiLink({
url,
contract: experimental_toContract(appContract),
}),
];Credits
experimental_openApiLink follows oRPC's OpenAPI link.