UNPKG

@trpc/client

Version:

The tRPC client library

318 lines (225 loc) • 7.84 kB
--- name: client-setup description: > Create a vanilla tRPC client with createTRPCClient<AppRouter>(), configure link chain with httpBatchLink/httpLink, dynamic headers for auth, transformer on links (not client constructor). Infer types with inferRouterInputs and inferRouterOutputs. AbortController signal support. TRPCClientError typing. type: core library: trpc library_version: '11.16.0' requires: - server-setup sources: - www/docs/client/overview.md - www/docs/client/vanilla/overview.md - www/docs/client/vanilla/setup.mdx - www/docs/client/vanilla/infer-types.md - www/docs/client/headers.md - packages/client/src/internals/TRPCUntypedClient.ts --- # tRPC -- Client Setup ## Setup ```ts // server.ts import { initTRPC } from '@trpc/server'; import { z } from 'zod'; const t = initTRPC.create(); const appRouter = t.router({ user: t.router({ byId: t.procedure .input(z.object({ id: z.string() })) .query(({ input }) => ({ id: input.id, name: 'Bilbo' })), create: t.procedure .input(z.object({ name: z.string() })) .mutation(({ input }) => ({ id: '1', ...input })), }), }); export type AppRouter = typeof appRouter; ``` ```ts // client.ts import { createTRPCClient, httpBatchLink } from '@trpc/client'; import type { AppRouter } from './server'; const client = createTRPCClient<AppRouter>({ links: [ httpBatchLink({ url: 'http://localhost:3000/trpc', }), ], }); const user = await client.user.byId.query({ id: '1' }); const created = await client.user.create.mutate({ name: 'Frodo' }); ``` ## Core Patterns ### Dynamic Auth Headers ```ts import { createTRPCClient, httpBatchLink } from '@trpc/client'; import type { AppRouter } from './server'; let token = ''; export function setToken(newToken: string) { token = newToken; } export const client = createTRPCClient<AppRouter>({ links: [ httpBatchLink({ url: 'http://localhost:3000/trpc', headers() { return { Authorization: token ? `Bearer ${token}` : '', }; }, }), ], }); ``` The `headers` callback is invoked on every HTTP request, so token changes take effect immediately. ### Inferring Procedure Input and Output Types ```ts import type { inferRouterInputs, inferRouterOutputs } from '@trpc/server'; import type { AppRouter } from './server'; type RouterInput = inferRouterInputs<AppRouter>; type RouterOutput = inferRouterOutputs<AppRouter>; type UserCreateInput = RouterInput['user']['create']; type UserByIdOutput = RouterOutput['user']['byId']; ``` ### Aborting Requests with AbortController ```ts import { createTRPCClient, httpBatchLink } from '@trpc/client'; import type { AppRouter } from './server'; const client = createTRPCClient<AppRouter>({ links: [httpBatchLink({ url: 'http://localhost:3000/trpc' })], }); const ac = new AbortController(); const query = client.user.byId.query({ id: '1' }, { signal: ac.signal }); ac.abort(); ``` ### Typed Error Handling ```ts import { TRPCClientError } from '@trpc/client'; import type { AppRouter } from './server'; function isTRPCClientError( cause: unknown, ): cause is TRPCClientError<AppRouter> { return cause instanceof TRPCClientError; } try { await client.user.byId.query({ id: '1' }); } catch (cause) { if (isTRPCClientError(cause)) { console.log('tRPC error code:', cause.data?.code); } } ``` ## Common Mistakes ### [CRITICAL] Missing AppRouter type parameter on createTRPCClient Wrong: ```ts const client = createTRPCClient({ links: [httpBatchLink({ url })] }); ``` Correct: ```ts import type { AppRouter } from './server'; const client = createTRPCClient<AppRouter>({ links: [httpBatchLink({ url })] }); ``` Without the type parameter, all procedure calls return `any` and type safety is completely lost. Source: www/docs/client/vanilla/setup.mdx ### [CRITICAL] Transformer goes on individual links, not createTRPCClient In v11, the `transformer` option is on individual terminating links, not the client constructor: ```ts import superjson from 'superjson'; createTRPCClient<AppRouter>({ links: [ httpBatchLink({ url: 'http://localhost:3000', transformer: superjson, }), ], }); ``` In v11, the `transformer` option was moved from the client constructor to individual terminating links. Passing it to `createTRPCClient` throws a TypeError. Source: packages/client/src/internals/TRPCUntypedClient.ts ### [CRITICAL] Transformer on server but not on client links Wrong: ```ts // Server: initTRPC.create({ transformer: superjson }) // Client: httpBatchLink({ url: 'http://localhost:3000' }); ``` Correct: ```ts // Server: initTRPC.create({ transformer: superjson }) // Client: httpBatchLink({ url: 'http://localhost:3000', transformer: superjson }); ``` If the server uses a transformer, every terminating link on the client must also specify that transformer. Mismatch causes "Unable to transform response" errors. Source: https://github.com/trpc/trpc/issues/7083 ### [CRITICAL] Using import instead of import type for AppRouter Wrong: ```ts import { AppRouter } from '../server/router'; ``` Correct: ```ts import type { AppRouter } from '../server/router'; ``` A non-type import pulls the entire server bundle into the client. Use `import type` so it is erased at build time. Source: www/docs/client/vanilla/setup.mdx ### [CRITICAL] Importing appRouter value to derive type in client Wrong: ```ts import { appRouter } from '../server/router'; type AppRouter = typeof appRouter; ``` Correct: ```ts // In server: export type AppRouter = typeof appRouter; // In client: import type { AppRouter } from '../server/router'; ``` Importing the `appRouter` value (not just the type) bundles the entire server into the client, shipping server code to the browser. Source: www/docs/server/routers.md ### [CRITICAL] Using type assertions to bypass AppRouter import errors Wrong: ```ts const client = createTRPCClient<any>({ links: [httpBatchLink({ url })] }); ``` Correct: ```ts // Fix the import path or monorepo configuration import type { AppRouter } from '@myorg/api-types'; const client = createTRPCClient<AppRouter>({ links: [httpBatchLink({ url })] }); ``` Casting to `any` or manually recreating the router type destroys end-to-end type safety. Fix the import path or monorepo config instead. Source: www/docs/client/vanilla/setup.mdx ### [CRITICAL] Using createTRPCProxyClient (renamed in v11) Wrong: ```ts import { createTRPCProxyClient } from '@trpc/client'; ``` Correct: ```ts import { createTRPCClient } from '@trpc/client'; ``` `createTRPCProxyClient` was renamed to `createTRPCClient` in v11. Source: www/docs/client/vanilla/setup.mdx ### [CRITICAL] Treating tRPC as a REST API Wrong: ```ts fetch('/api/trpc/users/123', { method: 'GET' }); ``` Correct: ```ts const user = await client.user.byId.query({ id: '123' }); // Raw equivalent: GET /api/trpc/user.byId?input={"id":"123"} ``` tRPC uses JSON-RPC over HTTP. Procedures are called by dot-separated name with JSON input, not by REST resource paths. Source: www/docs/client/overview.md ### [HIGH] HTML error page instead of JSON response If you see `couldn't parse JSON, invalid character '<'`, the tRPC endpoint returned an HTML page (404/503) instead of JSON. This means the `url` in your link config is wrong or infrastructure routing is misconfigured -- it is not a tRPC bug. Verify the URL matches your adapter's mount point. Source: www/docs/client/vanilla/setup.mdx ## See Also - `links` -- configure httpBatchLink, httpLink, splitLink, and other link types - `superjson` -- set up SuperJSON transformer on server and client - `server-setup` -- define routers, procedures, context, and export AppRouter type - `react-query-setup` -- use tRPC with TanStack React Query for React applications