import type { HttpRequest, HttpResponse, TemplatedApp } from 'uWebSockets.js'; import type { AnyRouter } from '@trpc/server'; import { type NodeHTTPCreateContextFnOptions, type NodeHTTPCreateContextOption, } from '@trpc/server/adapters/node-http'; import { resolveResponse, type HTTPBaseHandlerOptions, type ResolveHTTPRequestOptionsContextFn, } from '@trpc/server/http'; import { decorateHttpResponse, HttpResponseDecorated, uWsSendResponseStreamed, uWsToRequest, } from './fetchCompat'; import { WebSocketConnection } from './websockets'; export interface CreateHandlerOptions { /** * Url path prefix where the tRPC server will be registered. * @default '' */ prefix?: string | undefined; /** * Specify if SSL is used. Set to true if your application is served over HTTPS. * @default false */ ssl?: boolean | undefined; /** * Maximum request body size in bytes. If the body is larger than this, the request will be aborted. * Null value allows for unlimited body size. * @default null */ maxBodySize?: number | undefined; trpcOptions: HandlerOptions; } export type CreateContextOptions = NodeHTTPCreateContextFnOptions< Request, HttpResponseDecorated > & { /** * Client must be passed along when the context is created. **/ client?: WebSocketConnection | undefined; }; /** * Applies tRPC request handler to uWebSockets app. * @param app - The uWebSockets application. * @param opts - Options for configuring the tRPC request handler. */ export function applyRequestHandler( app: TemplatedApp, opts: CreateHandlerOptions ) { const prefix = opts.prefix ?? ''; const handler = async (res: HttpResponse, req: HttpRequest) => { const url = req.getUrl().substring(prefix.length + 1); const resDecorated = decorateHttpResponse(res, opts.ssl); const reqFetch = uWsToRequest(req, resDecorated, { maxBodySize: opts.maxBodySize ?? null, }); await uWsRequestHandler({ ...opts.trpcOptions, req: reqFetch, res: resDecorated, path: url, }); }; app.get(prefix + '/*', handler); app.post(prefix + '/*', handler); } export type HandlerOptions< TRouter extends AnyRouter, TRequest extends Request, TResponse extends HttpResponseDecorated, > = HTTPBaseHandlerOptions & NodeHTTPCreateContextOption; type RequestHandlerOptions< TRouter extends AnyRouter, TRequest extends Request, TResponse extends HttpResponseDecorated, > = HandlerOptions & { req: TRequest; res: TResponse; path: string; }; export async function uWsRequestHandler< TRouter extends AnyRouter, TRequest extends Request, TResponse extends HttpResponseDecorated, >(opts: RequestHandlerOptions) { const createContext: ResolveHTTPRequestOptionsContextFn = async ( innerOpts ) => { return await opts.createContext?.({ ...opts, ...innerOpts, }); }; const fetchRes = await resolveResponse({ ...opts, req: opts.req, error: null, createContext, onError(o) { opts?.onError?.({ ...o, req: opts.req, }); }, }); await uWsSendResponseStreamed(fetchRes, opts.res); }