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 { createError, defineEventHandler, isMethod, readBody } 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 export interface ResolveHTTPRequestOptions { router: TRouter createContext?: CreateContextFn responseMeta?: ResponseMetaFn onError?: OnErrorFn batching?: { enabled: boolean } } 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, batching }: ResolveHTTPRequestOptions) { 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(error) }) } const httpResponse = await resolveHTTPResponse({ batching, router, req: { method: req.method!, headers: req.headers, body: isMethod(event, 'GET') ? null : await readBody(event), query: $url.searchParams }, path, createContext: async () => await 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 }) }