Skip to content

REST Overrides

When naming conventions don’t fit your needs, use .rest() to customize the generated REST endpoints.

loginUser: procedure()
.input(z.object({ email: z.string(), password: z.string() }))
.mutation(handler)
.rest({
method: 'POST',
path: '/auth/login',
}),

Force a specific HTTP method:

.rest({ method: 'POST' }) // POST instead of inferred
.rest({ method: 'GET' }) // GET instead of inferred

Custom path (overrides resource-based path):

// Without override: POST /api/users
// With override: POST /auth/register
.rest({ path: '/auth/register' })

Include params in the path:

verifyEmail: procedure()
.input(z.object({ token: z.string() }))
.mutation(handler)
.rest({
method: 'GET',
path: '/verify/:token',
}),
// GET /api/verify/abc123

Keep procedure tRPC-only:

internalMetrics: procedure()
.query(handler)
.rest(false),
procedures('auth', {
register: procedure()
.input(RegisterSchema)
.mutation(handler)
.rest({ method: 'POST', path: '/auth/register' }),
login: procedure()
.input(LoginSchema)
.mutation(handler)
.rest({ method: 'POST', path: '/auth/login' }),
logout: procedure()
.mutation(handler)
.rest({ method: 'POST', path: '/auth/logout' }),
refreshToken: procedure()
.input(z.object({ refreshToken: z.string() }))
.mutation(handler)
.rest({ method: 'POST', path: '/auth/refresh' }),
});
procedures('comments', {
listPostComments: procedure()
.input(z.object({ postId: z.string() }))
.query(handler)
.rest({
method: 'GET',
path: '/posts/:postId/comments',
}),
createPostComment: procedure()
.input(z.object({ postId: z.string(), content: z.string() }))
.mutation(handler)
.rest({
method: 'POST',
path: '/posts/:postId/comments',
}),
});
procedures('users', {
bulkCreate: procedure()
.input(z.object({ users: z.array(CreateUserSchema) }))
.mutation(handler)
.rest({
method: 'POST',
path: '/users/bulk',
}),
bulkDelete: procedure()
.input(z.object({ ids: z.array(z.string()) }))
.mutation(handler)
.rest({
method: 'DELETE',
path: '/users/bulk',
}),
});
procedures('orders', {
cancelOrder: procedure()
.input(z.object({ id: z.string() }))
.mutation(handler)
.rest({
method: 'POST',
path: '/orders/:id/cancel',
}),
shipOrder: procedure()
.input(z.object({ id: z.string(), trackingNumber: z.string() }))
.mutation(handler)
.rest({
method: 'POST',
path: '/orders/:id/ship',
}),
});

Path params are automatically merged into input:

getOrderItem: procedure()
.input(z.object({
orderId: z.string(),
itemId: z.string(),
}))
.query(({ input }) => {
// input.orderId and input.itemId available
})
.rest({
method: 'GET',
path: '/orders/:orderId/items/:itemId',
}),