Skip to content

tRPC v12 isn’t on npm yet. The @trpc12/* packages on this site can’t be installed until it’s released. Use v11 today

tRPC

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/1

Declared 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.