diff --git a/src/routeTree.gen.ts b/src/routeTree.gen.ts index 2be49ea..03a4520 100644 --- a/src/routeTree.gen.ts +++ b/src/routeTree.gen.ts @@ -11,6 +11,7 @@ import { Route as rootRouteImport } from './routes/__root' import { Route as TodoRouteImport } from './routes/todo' import { Route as IndexRouteImport } from './routes/index' +import { Route as ApiRpcSplatRouteImport } from './routes/api/rpc.$' const TodoRoute = TodoRouteImport.update({ id: '/todo', @@ -22,31 +23,40 @@ const IndexRoute = IndexRouteImport.update({ path: '/', getParentRoute: () => rootRouteImport, } as any) +const ApiRpcSplatRoute = ApiRpcSplatRouteImport.update({ + id: '/api/rpc/$', + path: '/api/rpc/$', + getParentRoute: () => rootRouteImport, +} as any) export interface FileRoutesByFullPath { '/': typeof IndexRoute '/todo': typeof TodoRoute + '/api/rpc/$': typeof ApiRpcSplatRoute } export interface FileRoutesByTo { '/': typeof IndexRoute '/todo': typeof TodoRoute + '/api/rpc/$': typeof ApiRpcSplatRoute } export interface FileRoutesById { __root__: typeof rootRouteImport '/': typeof IndexRoute '/todo': typeof TodoRoute + '/api/rpc/$': typeof ApiRpcSplatRoute } export interface FileRouteTypes { fileRoutesByFullPath: FileRoutesByFullPath - fullPaths: '/' | '/todo' + fullPaths: '/' | '/todo' | '/api/rpc/$' fileRoutesByTo: FileRoutesByTo - to: '/' | '/todo' - id: '__root__' | '/' | '/todo' + to: '/' | '/todo' | '/api/rpc/$' + id: '__root__' | '/' | '/todo' | '/api/rpc/$' fileRoutesById: FileRoutesById } export interface RootRouteChildren { IndexRoute: typeof IndexRoute TodoRoute: typeof TodoRoute + ApiRpcSplatRoute: typeof ApiRpcSplatRoute } declare module '@tanstack/react-router' { @@ -65,12 +75,20 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } + '/api/rpc/$': { + id: '/api/rpc/$' + path: '/api/rpc/$' + fullPath: '/api/rpc/$' + preLoaderRoute: typeof ApiRpcSplatRouteImport + parentRoute: typeof rootRouteImport + } } } const rootRouteChildren: RootRouteChildren = { IndexRoute: IndexRoute, TodoRoute: TodoRoute, + ApiRpcSplatRoute: ApiRpcSplatRoute, } export const routeTree = rootRouteImport ._addFileChildren(rootRouteChildren) diff --git a/src/routes/api/rpc.$.ts b/src/routes/api/rpc.$.ts new file mode 100644 index 0000000..18fa6f9 --- /dev/null +++ b/src/routes/api/rpc.$.ts @@ -0,0 +1,70 @@ +import { ORPCError, onError, ValidationError } from '@orpc/server' +import { RPCHandler } from '@orpc/server/fetch' +import { createFileRoute } from '@tanstack/react-router' +import { z } from 'zod' +import { router } from '@/orpc/router' + +const handler = new RPCHandler(router, { + interceptors: [ + onError((error) => { + console.error(error) + }), + ], + clientInterceptors: [ + onError((error) => { + if ( + error instanceof ORPCError && + error.code === 'BAD_REQUEST' && + error.cause instanceof ValidationError + ) { + // If you only use Zod you can safely cast to ZodIssue[] + const zodError = new z.ZodError( + error.cause.issues as z.core.$ZodIssue[], + ) + + throw new ORPCError('INPUT_VALIDATION_FAILED', { + status: 422, + message: z.prettifyError(zodError), + data: z.flattenError(zodError), + cause: error.cause, + }) + } + + if ( + error instanceof ORPCError && + error.code === 'INTERNAL_SERVER_ERROR' && + error.cause instanceof ValidationError + ) { + throw new ORPCError('OUTPUT_VALIDATION_FAILED', { + cause: error.cause, + }) + } + }), + ], +}) + +async function handle({ request }: { request: Request }) { + const { matched, response } = await handler.handle(request, { + prefix: '/api/rpc', + context: {}, + }) + + if (matched) { + return response + } + + return new Response('Not Found', { status: 404 }) +} + +export const Route = createFileRoute('/api/rpc/$')({ + server: { + handlers: { + HEAD: handle, + GET: handle, + POST: handle, + PUT: handle, + PATCH: handle, + DELETE: handle, + }, + }, +})