Skip to content
tRPC

Links

A client's chain of links, the built-in ones, and the order they go in.

On this page

A client's links is a chain: each link gets the operation and the rest of the chain. The links in the middle route, retry, share or log operations; the last one, the terminating link, carries them to a server or runs them in process.

import {
  function createTrpcClient<TSource, const TLinks extends ReadonlyArray<TrpcLink<any, RouterOf<TSource>>>>(opts: TypedClientOptions<TSource, TLinks> & DuplicateSwitchCheck<TLinks> & UnmetNeedCheck<TLinks>): TrpcClient<RouterOf<TSource>, LinksContext<TLinks>, undefined, LinksTypes<TLinks>>createTrpcClient,
  
function dedupeLink(opts?: DedupeLinkOptions): TrpcLink<{
    context: DedupeContext;
}>
dedupeLink
,
function httpLink<TOpts extends HttpLinkOptions>(opts: TOpts): TrpcLink<HttpLinkDecl<TOpts>>httpLink,
function loggerLink(opts?: {
    enabled?: (op: Operation) => boolean;
    log?: (event: LoggerEvent) => void;
}): TrpcLink
loggerLink
,
function retryLink(opts?: RetryLinkOptions): TrpcLinkretryLink, function routerType<TRouter>(): RouterType<TRouter>routerType, } from '@trpc12/trpc/client'; const
const client: TrpcClient<{
    post: {
        byId: BuildProcedure<InputDef<RootDef<Context, {}>, ZodObject<{
            id: ZodString;
        }, $strip>>, "query", {
            output: Post;
            errors: TrpcError<"NOT_FOUND", {
                id: string;
            }>;
            services: never;
            unmapped: never;
        }>;
        list: BuildProcedure<RootDef<Context, {}>, "query", {
            output: Post[];
            errors: never;
            services: never;
            unmapped: never;
        }>;
        create: BuildProcedure<InputDef<With<RootDef<Context, {}>, {
            ctx: Context;
            ctxAdded: {
                ...;
            };
            inputOut: Unset;
            errors: TrpcError<...>;
            shorts: never;
        }>, ZodObject<...>>, "mutation", {
            ...;
        }>;
    };
}, {
    ...;
}, undefined, LinksTypes<...>>
client
=
createTrpcClient<RouterType<{
    post: {
        byId: BuildProcedure<InputDef<RootDef<Context, {}>, ZodObject<{
            id: ZodString;
        }, $strip>>, "query", {
            output: Post;
            errors: TrpcError<"NOT_FOUND", {
                id: string;
            }>;
            services: never;
            unmapped: never;
        }>;
        list: BuildProcedure<RootDef<Context, {}>, "query", {
            output: Post[];
            errors: never;
            services: never;
            unmapped: never;
        }>;
        create: BuildProcedure<InputDef<With<RootDef<Context, {}>, {
            ctx: Context;
            ctxAdded: {
                ...;
            };
            inputOut: Unset;
            errors: TrpcError<...>;
            shorts: never;
        }>, ZodObject<...>>, "mutation", {
            ...;
        }>;
    };
}>, readonly [...]>(opts: TypedClientOptions<...>): TrpcClient<...>
createTrpcClient
({
TypedClientOptions<RouterType<{ post: { byId: BuildProcedure<InputDef<RootDef<Context, {}>, ZodObject<{ id: ZodString; }, $strip>>, "query", { output: Post; errors: TrpcError<...>; services: never; unmapped: never; }>; list: BuildProcedure<...>; create: BuildProcedure<...>; }; }>, readonly [...]>.router: RouterType<{
    post: {
        byId: BuildProcedure<InputDef<RootDef<Context, {}>, ZodObject<{
            id: ZodString;
        }, $strip>>, "query", {
            output: Post;
            errors: TrpcError<"NOT_FOUND", {
                id: string;
            }>;
            services: never;
            unmapped: never;
        }>;
        list: BuildProcedure<RootDef<Context, {}>, "query", {
            output: Post[];
            errors: never;
            services: never;
            unmapped: never;
        }>;
        create: BuildProcedure<InputDef<With<RootDef<Context, {}>, {
            ctx: Context;
            ctxAdded: {
                ...;
            };
            inputOut: Unset;
            errors: TrpcError<...>;
            shorts: never;
        }>, ZodObject<...>>, "mutation", {
            ...;
        }>;
    };
}>
router
:
routerType<{
    post: {
        byId: BuildProcedure<InputDef<RootDef<Context, {}>, ZodObject<{
            id: ZodString;
        }, $strip>>, "query", {
            output: Post;
            errors: TrpcError<"NOT_FOUND", {
                id: string;
            }>;
            services: never;
            unmapped: never;
        }>;
        list: BuildProcedure<RootDef<Context, {}>, "query", {
            output: Post[];
            errors: never;
            services: never;
            unmapped: never;
        }>;
        create: BuildProcedure<InputDef<With<RootDef<Context, {}>, {
            ctx: Context;
            ctxAdded: {
                ...;
            };
            inputOut: Unset;
            errors: TrpcError<...>;
            shorts: never;
        }>, ZodObject<...>>, "mutation", {
            ...;
        }>;
    };
}>(): RouterType<...>
routerType
<
type AppRouter = {
    post: {
        byId: BuildProcedure<InputDef<RootDef<Context, {}>, ZodObject<{
            id: ZodString;
        }, $strip>>, "query", {
            output: Post;
            errors: TrpcError<"NOT_FOUND", {
                id: string;
            }>;
            services: never;
            unmapped: never;
        }>;
        list: BuildProcedure<RootDef<Context, {}>, "query", {
            output: Post[];
            errors: never;
            services: never;
            unmapped: never;
        }>;
        create: BuildProcedure<InputDef<With<RootDef<Context, {}>, {
            ctx: Context;
            ctxAdded: {
                ...;
            };
            inputOut: Unset;
            errors: TrpcError<...>;
            shorts: never;
        }>, ZodObject<...>>, "mutation", {
            ...;
        }>;
    };
}
AppRouter
>(),
TypedClientOptions<RouterType<{ post: { byId: BuildProcedure<InputDef<RootDef<Context, {}>, ZodObject<{ id: ZodString; }, $strip>>, "query", { output: Post; errors: TrpcError<...>; services: never; unmapped: never; }>; list: BuildProcedure<...>; create: BuildProcedure<...>; }; }>, readonly [...]>.links: readonly [TrpcLink<{}, any>, TrpcLink<{
    context: DedupeContext;
}, any>, TrpcLink<{}, any>, TrpcLink<HttpLinkDecl<{
    url: string;
    batch: true;
}>, any>]
links
: [
function loggerLink(opts?: {
    enabled?: (op: Operation) => boolean;
    log?: (event: LoggerEvent) => void;
}): TrpcLink
loggerLink
({ enabled?: ((op: Operation) => boolean) | undefinedenabled: () => var process: NodeJS.Processprocess.NodeJS.Process.env: NodeJS.ProcessEnvenv.string | undefinedNODE_ENV === 'development' }),
function dedupeLink(opts?: DedupeLinkOptions): TrpcLink<{
    context: DedupeContext;
}>
dedupeLink
(),
function retryLink(opts?: RetryLinkOptions): TrpcLinkretryLink(),
httpLink<{
    url: string;
    batch: true;
}>(opts: {
    url: string;
    batch: true;
}): TrpcLink<HttpLinkDecl<{
    url: string;
    batch: true;
}>, any>
httpLink
({ url: stringurl: 'http://localhost:3000/trpc', batch: truebatch: true }),
], });

An operation goes through the links in order and its results come back the other way. Here the logger sees each call once, and identical queries are shared before the retries, so a retried query is still one request for every caller.

LinkCarries calls
httpLinkOver HTTP: queries as GET, mutations as POST, subscriptions as SSE
wsLinkOver one WebSocket
localLinkTo a router in the same process
experimental_socketLinkOver a raw TCP, TLS or Unix socket (Raw sockets)
experimental_messagePortLinkTo a worker or a MessagePort (Message ports)
openApiLinkTo each procedure's REST route (Calling REST)
LinkDoes
splitLinkPicks one of two chains per call
switchLinkPicks one of several named chains, and a call can force one
retryLinkRetries failed calls
dedupeLinkShares identical queries in flight
loggerLinkLogs every operation, its results and its errors
validationLinkChecks inputs and results against a contract
experimental_durableLinkRetries lost responses with the same call id (Durable calls)

Call options

A link declares the call options it reads (dedupe for dedupeLink, a switchLink's name), and a client made with router: types them on every call, next to the built-in signal, headers, init, batch and reconnect (The client).

link(({ op, next }) => stream) writes a link of your own, with the call options it reads, the errors it fails with and what it needs from the links after it (Custom links).

Pages under Links