import type { ResponseMeta } from '@trpc/server/http' import { resolveHTTPResponse } from '@trpc/server/http' import type { AnyRouter, ProcedureType, inferRouterContext, inferRouterError, } from '@trpc/server' import { TRPCError, } from '@trpc/server' import { createURL } from 'ufo' import type { H3Event } from 'h3' import { defineEventHandler, isMethod, readBody, createError } from 'h3' import type { TRPCResponse } from '@trpc/server/rpc' type MaybePromise = T | Promise export type CreateContextFn = (event: H3Event) => MaybePromise> export interface ResponseMetaFnPayload { data: TRPCResponse>[] ctx?: inferRouterContext paths?: string[] type: ProcedureType | 'unknown' errors: TRPCError[] } export type ResponseMetaFn = (opts: ResponseMetaFnPayload) => ResponseMeta export interface OnErrorPayload { error: TRPCError type: ProcedureType | 'unknown' path: string | undefined req: H3Event['req'] input: unknown ctx: undefined | inferRouterContext } export type OnErrorFn = (opts: OnErrorPayload) => void function getPath(event: H3Event): string | null { if (typeof event.context.params.trpc === 'string') return event.context.params.trpc if (Array.isArray(event.context.params.trpc)) return event.context.params.trpc.join('/') return null } export function createNuxtApiHandler({ router, createContext, responseMeta, onError, }: { router: TRouter createContext?: CreateContextFn responseMeta?: ResponseMetaFn onError?: OnErrorFn }) { return defineEventHandler(async (event) => { const { req, res, } = event const $url = createURL(req.url!) const path = getPath(event) if (path === null) { const error = router.getErrorShape({ error: new TRPCError({ message: 'Param "trpc" not found - is the file named `[trpc]`.ts or `[...trpc].ts`?', code: 'INTERNAL_SERVER_ERROR', }), type: 'unknown', ctx: undefined, path: undefined, input: undefined, }) throw createError({ statusCode: 500, statusMessage: JSON.stringify({ id: -1, error, }), }) } const httpResponse = await resolveHTTPResponse({ router, req: { method: req.method!, headers: req.headers, body: isMethod(event, 'GET') ? null : await readBody(event), query: $url.searchParams, }, path, createContext: async () => createContext?.(event), responseMeta, onError: (o) => { onError?.({ ...o, req, }) }, }) const { status, headers, body } = httpResponse res.statusCode = status headers && Object.keys(headers).forEach((key) => { res.setHeader(key, headers[key]!) }) return body }) }