feat: 添加RPC API路由支持与Zod验证错误响应
- 添加RPC API路由处理,支持多种HTTP方法并集成Zod验证错误的自定义错误响应。 - 添加对 `/api/rpc/$` 路由路径的支持,包括路由配置、类型定义和路由树的更新。
This commit is contained in:
@@ -11,6 +11,7 @@
|
|||||||
import { Route as rootRouteImport } from './routes/__root'
|
import { Route as rootRouteImport } from './routes/__root'
|
||||||
import { Route as TodoRouteImport } from './routes/todo'
|
import { Route as TodoRouteImport } from './routes/todo'
|
||||||
import { Route as IndexRouteImport } from './routes/index'
|
import { Route as IndexRouteImport } from './routes/index'
|
||||||
|
import { Route as ApiRpcSplatRouteImport } from './routes/api/rpc.$'
|
||||||
|
|
||||||
const TodoRoute = TodoRouteImport.update({
|
const TodoRoute = TodoRouteImport.update({
|
||||||
id: '/todo',
|
id: '/todo',
|
||||||
@@ -22,31 +23,40 @@ const IndexRoute = IndexRouteImport.update({
|
|||||||
path: '/',
|
path: '/',
|
||||||
getParentRoute: () => rootRouteImport,
|
getParentRoute: () => rootRouteImport,
|
||||||
} as any)
|
} as any)
|
||||||
|
const ApiRpcSplatRoute = ApiRpcSplatRouteImport.update({
|
||||||
|
id: '/api/rpc/$',
|
||||||
|
path: '/api/rpc/$',
|
||||||
|
getParentRoute: () => rootRouteImport,
|
||||||
|
} as any)
|
||||||
|
|
||||||
export interface FileRoutesByFullPath {
|
export interface FileRoutesByFullPath {
|
||||||
'/': typeof IndexRoute
|
'/': typeof IndexRoute
|
||||||
'/todo': typeof TodoRoute
|
'/todo': typeof TodoRoute
|
||||||
|
'/api/rpc/$': typeof ApiRpcSplatRoute
|
||||||
}
|
}
|
||||||
export interface FileRoutesByTo {
|
export interface FileRoutesByTo {
|
||||||
'/': typeof IndexRoute
|
'/': typeof IndexRoute
|
||||||
'/todo': typeof TodoRoute
|
'/todo': typeof TodoRoute
|
||||||
|
'/api/rpc/$': typeof ApiRpcSplatRoute
|
||||||
}
|
}
|
||||||
export interface FileRoutesById {
|
export interface FileRoutesById {
|
||||||
__root__: typeof rootRouteImport
|
__root__: typeof rootRouteImport
|
||||||
'/': typeof IndexRoute
|
'/': typeof IndexRoute
|
||||||
'/todo': typeof TodoRoute
|
'/todo': typeof TodoRoute
|
||||||
|
'/api/rpc/$': typeof ApiRpcSplatRoute
|
||||||
}
|
}
|
||||||
export interface FileRouteTypes {
|
export interface FileRouteTypes {
|
||||||
fileRoutesByFullPath: FileRoutesByFullPath
|
fileRoutesByFullPath: FileRoutesByFullPath
|
||||||
fullPaths: '/' | '/todo'
|
fullPaths: '/' | '/todo' | '/api/rpc/$'
|
||||||
fileRoutesByTo: FileRoutesByTo
|
fileRoutesByTo: FileRoutesByTo
|
||||||
to: '/' | '/todo'
|
to: '/' | '/todo' | '/api/rpc/$'
|
||||||
id: '__root__' | '/' | '/todo'
|
id: '__root__' | '/' | '/todo' | '/api/rpc/$'
|
||||||
fileRoutesById: FileRoutesById
|
fileRoutesById: FileRoutesById
|
||||||
}
|
}
|
||||||
export interface RootRouteChildren {
|
export interface RootRouteChildren {
|
||||||
IndexRoute: typeof IndexRoute
|
IndexRoute: typeof IndexRoute
|
||||||
TodoRoute: typeof TodoRoute
|
TodoRoute: typeof TodoRoute
|
||||||
|
ApiRpcSplatRoute: typeof ApiRpcSplatRoute
|
||||||
}
|
}
|
||||||
|
|
||||||
declare module '@tanstack/react-router' {
|
declare module '@tanstack/react-router' {
|
||||||
@@ -65,12 +75,20 @@ declare module '@tanstack/react-router' {
|
|||||||
preLoaderRoute: typeof IndexRouteImport
|
preLoaderRoute: typeof IndexRouteImport
|
||||||
parentRoute: typeof rootRouteImport
|
parentRoute: typeof rootRouteImport
|
||||||
}
|
}
|
||||||
|
'/api/rpc/$': {
|
||||||
|
id: '/api/rpc/$'
|
||||||
|
path: '/api/rpc/$'
|
||||||
|
fullPath: '/api/rpc/$'
|
||||||
|
preLoaderRoute: typeof ApiRpcSplatRouteImport
|
||||||
|
parentRoute: typeof rootRouteImport
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const rootRouteChildren: RootRouteChildren = {
|
const rootRouteChildren: RootRouteChildren = {
|
||||||
IndexRoute: IndexRoute,
|
IndexRoute: IndexRoute,
|
||||||
TodoRoute: TodoRoute,
|
TodoRoute: TodoRoute,
|
||||||
|
ApiRpcSplatRoute: ApiRpcSplatRoute,
|
||||||
}
|
}
|
||||||
export const routeTree = rootRouteImport
|
export const routeTree = rootRouteImport
|
||||||
._addFileChildren(rootRouteChildren)
|
._addFileChildren(rootRouteChildren)
|
||||||
|
|||||||
70
src/routes/api/rpc.$.ts
Normal file
70
src/routes/api/rpc.$.ts
Normal file
@@ -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,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
Reference in New Issue
Block a user