honojs-middleware/packages/trpc-server
Jonathan Haines 4d67af162f
test(workspace): upgrade to vitest v3 (#1009)
* test(workspace): upgrade to vitest v3

Fixes #1007

* chore(standard-validator): add vitest type to `tsconfig.json`

* chore: update `yarn.lock`

* chore(zod-openapi): bump `typescript`

* chore(typia-validator): make it ESM

* ci(bun-transpiler): fix Bun to v1.1.32

---------

Co-authored-by: Yusuke Wada <yusuke@kamawada.com>
2025-03-12 12:52:15 +09:00
..
src chore: Migrate workspaces that use `jest` to `vitest` (#998) 2025-03-04 22:00:28 +09:00
CHANGELOG.md Version Packages (#867) 2024-12-03 10:06:18 +09:00
README.md docs(trpc): add custom endpoint configuration example (#538) 2024-05-23 22:03:33 +09:00
package.json test(workspace): upgrade to vitest v3 (#1009) 2025-03-12 12:52:15 +09:00
tsconfig.cjs.json feat: introduce tRPC server middleware (#36) 2023-02-04 23:37:15 +09:00
tsconfig.esm.json feat: introduce tRPC server middleware (#36) 2023-02-04 23:37:15 +09:00
tsconfig.json chore: Migrate workspaces that use `jest` to `vitest` (#998) 2025-03-04 22:00:28 +09:00
vitest.config.ts test(workspace): upgrade to vitest v3 (#1009) 2025-03-12 12:52:15 +09:00

README.md

tRPC Server Middleware for Hono

tRPC Server Middleware adapts a tRPC server as middleware for Hono. Hono works on almost any JavaScript runtime, including Cloudflare Workers, Deno, and Bun. So, with this middleware, the same code will run as tRPC server.

Install

npm install @hono/trpc-server

Usage

Router:

import { initTRPC } from '@trpc/server'
import { z } from 'zod'

const t = initTRPC.create()

const publicProcedure = t.procedure
const router = t.router

export const appRouter = router({
  hello: publicProcedure.input(z.string().nullish()).query(({ input }) => {
    return `Hello ${input ?? 'World'}!`
  }),
})

export type AppRouter = typeof appRouter

Hono app using tRPC Server Middleware:

import { Hono } from 'hono'
import { trpcServer } from '@hono/trpc-server' // Deno 'npm:@hono/trpc-server'
import { appRouter } from './router'

const app = new Hono()

app.use(
  '/trpc/*',
  trpcServer({
    router: appRouter,
  })
)

export default app

Client:

import { createTRPCProxyClient, httpBatchLink } from '@trpc/client'
import type { AppRouter } from './router'

const client = createTRPCProxyClient<AppRouter>({
  links: [
    httpBatchLink({
      url: 'http://localhost:8787/trpc',
    }),
  ],
})

console.log(await client.hello.query('Hono'))

Context

You can also access c.env from hono context from the trpc ctx. eg. here's an example using cloudflare D1 binding available as env.DB

import { initTRPC } from '@trpc/server'
import { z } from 'zod'

type Env = {
  DB: D1Database;
}
type HonoContext = {
  env: Env,
};

const t = initTRPC.context<HonoContext>().create()

const publicProcedure = t.procedure
const router = t.router

export const appRouter = router({
  usersCount: publicProcedure.query(({ input, ctx }) => {
    const result = await ctx.env.DB.prepare("SELECT count(*) from user;").all();
    return result.results[0].count;
  }),
})

export type AppRouter = typeof appRouter

For further control, you can optionally specify a createContext that in this case will receive the hono context as 2nd argument:

app.use(
  '/trpc/*',
  trpcServer({
    router: appRouter,
    createContext: (_opts, c) => ({
      // c is the hono context
      var1: c.env.MY_VAR1,
      var2: c.req.header('X-VAR2'),
    })
  })
)

Custom Endpoints

To set up custom endpoints ensure the endpoint parameter matches the middleware's path. This alignment allows @trpc/server to accurately extract your procedure paths.

import { Hono } from 'hono'
import { trpcServer } from '@hono/trpc-server'
import { appRouter } from './router'

const app = new Hono()

// Custom endpoint configuration
app.use(
  '/api/trpc/*',
  trpcServer({
    endpoint: '/api/trpc',
    router: appRouter,
  })
)

export default app

Author

Yusuke Wada https://github.com/yusukebe

License

MIT