Skip to main content
Version: 10.x

Server Side Calls

You may need to call your procedure(s) directly from the server, createCaller() function returns you an instance of RouterCaller able to execute query(ies) and mutation(s).

Create caller

In every examples, after tRPC initialization, we generate the router.
Then with router.createCaller({}) function (param of this Context) we retrieve an instance of RouterCaller.

Input query example

We create the router with a input query and then we call the asynchronous greeting procedure to get the result.

ts
import { initTRPC } from '@trpc/server';
const t = initTRPC.create();
const router = t.router({
// Create procedure at path 'greeting'
greeting: t.procedure
.input(z.object({ name: z.string() }))
.query(({ input }) => `Hello ${input.name}`),
});
const caller = router.createCaller({});
const result = await caller.greeting({ name: 'tRPC' });
ts
import { initTRPC } from '@trpc/server';
const t = initTRPC.create();
const router = t.router({
// Create procedure at path 'greeting'
greeting: t.procedure
.input(z.object({ name: z.string() }))
.query(({ input }) => `Hello ${input.name}`),
});
const caller = router.createCaller({});
const result = await caller.greeting({ name: 'tRPC' });

Mutation example

We create the router with a mutation and then we call the asynchronous post procedure to get the result.

ts
import { z } from 'zod';
import { initTRPC } from '@trpc/server';
const posts = ['One', 'Two', 'Three'];
const t = initTRPC.create();
const router = t.router({
post: t.router({
delete: t.procedure.input(z.number()).mutation(({ input }) => {
posts.splice(input, 1);
}),
}),
});
const caller = router.createCaller({});
await caller.post.delete(0);
ts
import { z } from 'zod';
import { initTRPC } from '@trpc/server';
const posts = ['One', 'Two', 'Three'];
const t = initTRPC.create();
const router = t.router({
post: t.router({
delete: t.procedure.input(z.number()).mutation(({ input }) => {
posts.splice(input, 1);
}),
}),
});
const caller = router.createCaller({});
await caller.post.delete(0);

Context with middleware example

We create a middleware to check the context before execute secret procedure. Below two examples, the former fails because the context doesn't fit the middleware logic the latter works correctly.


info
  • Middleware(s) are performed before any procedure(s).

ts
import { TRPCError, initTRPC } from '@trpc/server';
const t = initTRPC.create();
const isAuthed = t.middleware(({ next, ctx }) => {
if (!ctx.foo) {
throw new TRPCError({
code: 'UNAUTHORIZED',
message: 'You are not authorized',
});
}
return next();
});
const protectedProcedure = t.procedure.use(isAuthed);
const router = t.router({
secret: protectedProcedure.query(({ ctx }) => ctx.foo),
});
// it returns an error because there isn't the right context param
const caller = router.createCaller({});
const result = await caller.secret();
// it works because foo property is present inside context param
const authorizedCaller = router.createCaller({ foo: 'bar' });
result = await authorizedCaller.secret();
ts
import { TRPCError, initTRPC } from '@trpc/server';
const t = initTRPC.create();
const isAuthed = t.middleware(({ next, ctx }) => {
if (!ctx.foo) {
throw new TRPCError({
code: 'UNAUTHORIZED',
message: 'You are not authorized',
});
}
return next();
});
const protectedProcedure = t.procedure.use(isAuthed);
const router = t.router({
secret: protectedProcedure.query(({ ctx }) => ctx.foo),
});
// it returns an error because there isn't the right context param
const caller = router.createCaller({});
const result = await caller.secret();
// it works because foo property is present inside context param
const authorizedCaller = router.createCaller({ foo: 'bar' });
result = await authorizedCaller.secret();