rembrembdocs

Methods <-> Type mapping

HTTP Method

Mapping

Notes

GET

.query()

Input JSON-stringified in query param.
e.g. myQuery?input=${encodeURIComponent(JSON.stringify(input))}

POST

.mutation()

Input as POST body.

GET

.subscription()

Subscriptions are supported via Server-sent Events using httpSubscriptionLink, or via WebSockets using wsLink.

Accessing nested procedures

Nested procedures are separated by dots, so a request to byId below would end up being a request to /api/trpc/post.byId.

ts

export const appRouter = router({

post: router({

`byId: publicProcedure.input(String).query(async (opts) => {`

  `// [...]`

`}),`

}),

});

Batching

When batching, we combine all parallel procedure calls of the same HTTP method in one request using a data loader.

Batching Example Request

Given a router like this exposed at /api/trpc:

server/router.ts

tsx

export const appRouter = t.router({

postById: t.procedure.input(String).query(async (opts) => {

`const post = await opts.ctx.post.findUnique({`

  `where: { id: opts.input },`

`});`

`return post;`

}),

relatedPosts: t.procedure.input(String).query(async (opts) => {

`const posts = await opts.ctx.findRelatedPostsById(opts.input);`

`return posts;`

}),

});

... And two queries defined like this in a React component:

MyComponent.tsx

tsx

export function MyComponent() {

const post1 = trpc.postById.useQuery('1');

const relatedPosts = trpc.relatedPosts.useQuery('1');

return (

`<pre>`

  `{JSON.stringify(`

    `{`

      `post1: post1.data ?? null,`

      `relatedPosts: relatedPosts.data ?? null,`

    `},`

    `null,`

    `4,`

  `)}`

`</pre>`

);

}

The above would result in exactly 1 HTTP call with this data:

Location property

Value

pathname

/api/trpc/postById,relatedPosts

search

?batch=1&input=%7B%220%22%3A%221%22%2C%221%22%3A%221%22%7D *

*) input in the above is the result of:

ts

encodeURIComponent(

JSON.stringify({

`` 0: '1', // <-- input for `postById` ``

`` 1: '1', // <-- input for `relatedPosts` ``

}),

);

Batching Example Response

Example output from server

HTTP Response Specification

In order to have a specification that works regardless of the transport layer we try to conform to JSON-RPC 2.0 where possible.

Successful Response

Example JSON Response

ts

interface SuccessResponse {

result: {

`data: TOutput; // output from procedure`

}

}

Error Response

Example JSON Response

Error Codes <-> HTTP Status

ts

const HTTP_STATUS_CODES = {

PARSE_ERROR: 400,

BAD_REQUEST: 400,

UNAUTHORIZED: 401,

PAYMENT_REQUIRED: 402,

FORBIDDEN: 403,

NOT_FOUND: 404,

METHOD_NOT_SUPPORTED: 405,

TIMEOUT: 408,

CONFLICT: 409,

PRECONDITION_FAILED: 412,

PAYLOAD_TOO_LARGE: 413,

UNSUPPORTED_MEDIA_TYPE: 415,

UNPROCESSABLE_CONTENT: 422,

PRECONDITION_REQUIRED: 428,

TOO_MANY_REQUESTS: 429,

CLIENT_CLOSED_REQUEST: 499,

INTERNAL_SERVER_ERROR: 500,

NOT_IMPLEMENTED: 501,

BAD_GATEWAY: 502,

SERVICE_UNAVAILABLE: 503,

GATEWAY_TIMEOUT: 504,

} as const;

Error Codes <-> JSON-RPC 2.0 Error Codes

Available codes & JSON-RPC code

Overriding the default HTTP method

To override the HTTP method used for queries/mutations, you can use the methodOverride option:

server/httpHandler.ts

tsx

// Your server must separately allow the client to override the HTTP method

const handler = createHTTPHandler({

router: router,

allowMethodOverride: true,

});

client/trpc.ts

tsx

import { createTRPCClient, httpLink } from '@trpc/client';

import type { AppRouter } from './server';

// The client can then specify which HTTP method to use for all queries/mutations

const client = createTRPCClient<AppRouter>({

links: [

`httpLink({`

  ``url: `http://localhost:3000`,``

  `methodOverride: 'POST', // all queries and mutations will be sent to the tRPC Server as POST requests.`

`}),`

],

});

Dig deeper

You can read more details by drilling into the TypeScript definitions in