Skip to content

Instantly share code, notes, and snippets.

@jaysson
Last active March 26, 2025 10:16
Show Gist options
  • Save jaysson/c2fffcd8539d57105e524b51cd38fce7 to your computer and use it in GitHub Desktop.
Save jaysson/c2fffcd8539d57105e524b51cd38fce7 to your computer and use it in GitHub Desktop.
TRPC Integration for Adonis
import Route from '@ioc:Adonis/Core/Route';
import { handleHttpRequest } from 'App/TRPC';
Route.any('/trpc/*', handleHttpRequest);
import { initTRPC } from '@trpc/server';
import { resolveHTTPResponse } from '@trpc/server/http';
import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext';
const t = initTRPC.create();
export const appRouter = t.router({
hello: t.procedure.query(() => {
return { message: 'World' };
}),
});
export const handleHttpRequest = async (ctx: HttpContextContract) => {
const { request, response } = ctx;
const url = new URL(request.completeUrl(true));
const path = url.pathname.slice('/trpc'.length + 1);
const { body, status, headers } = await resolveHTTPResponse({
createContext: async () => ctx,
router: appRouter,
path,
req: {
query: url.searchParams,
method: request.method(),
headers: request.headers(),
body: request.body(),
},
});
if (headers) {
Object.keys(headers).forEach((key) => {
const value = headers[key];
if (value) response.header(key, value);
});
}
response.status(status);
response.send(body);
}
export type AppRouter = typeof appRouter;
@ThBM
Copy link

ThBM commented Feb 1, 2023

Thanks for sharing!

I would modify the t initialization for type inference of HttpContextContract.

const t = initTRPC.context<HttpContextContract>().create();

@vinicioslc
Copy link

@ThBM oh this is nice thanks !

@xferqr
Copy link

xferqr commented Oct 29, 2024

for tRPC ^11.0.0-rc.593 / Adonis.js v5

import { fetchRequestHandler } from '@trpc/server/adapters/fetch'
import { HttpContextContract } from '@ioc:Adonis/Core/HttpContext'

export const handleHttpRequest = async (ctx: HttpContextContract) => {
  const { request, response } = ctx

  const result = await fetchRequestHandler({
    endpoint: '/trpc',
    req: new Request(request.completeUrl(true), {
      headers: request.headers(),
      body: request.method() !== 'GET' ? JSON.stringify(request.body()) : undefined,
      method: request.method()
    }),
    router: createRouter(),
    createContext: async () => ctx
  })

  const responseText = await new Response(result.body).text()

  return response.status(result.status).header('content-type', 'application/json').send(responseText)
}

@nidomiro
Copy link

nidomiro commented Mar 26, 2025

I just got a working version for adonisjs 6.17.2 and trpc 11.0.0:

import { HttpContext } from '@adonisjs/core/http'
import { initTRPC } from '@trpc/server'
import { resolveResponse } from '@trpc/server/http'

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

export const appRouter = t.router({
  hello: t.procedure.query(() => {
    return { message: 'World' }
  }),
})

export const endpoint = '/trpc' as const

export const trpcHTTPRequestHandler = async (ctx: HttpContext) => {
  const { request, response } = ctx

  const url = new URL(request.completeUrl(true))
  const path = url.pathname.slice(endpoint.length).replace(/^[/]+|[/]+$/g, '')

  const trpcRequest = new Request(url, {
    headers: request.headers() as Record<string, string | ReadonlyArray<string>>,
    body: request.raw(),
    method: request.method(),
  })

  const trpcResponse = await resolveResponse({
    createContext: async () => ctx,
    router: appRouter,
    path,
    req: trpcRequest,
    error: null,
  })

  const trpcResponseText = await trpcResponse.text()

  trpcResponse.headers.forEach((value, key) => {
    response.header(key, value)
  })

  response.status(trpcResponse.status)
  response.send(trpcResponseText)
}

export type AppRouter = typeof appRouter

and my route declaration:

router.any(`${trpcEndpoint}/*`, trpcHTTPRequestHandler)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment