-
transformer
-
Data Transformers,序列化响应数据和输入的参数
- 安装 superjson,版本 "superjson": "^2.2.2"
- 修改文件
src/trpc/client.tsx,src/trpc/query-client.tsx ,通过 import superjson from 'superjson'; 添加 superjson
-
Add auth to tRPC context
...
import superjson from 'superjson';
import { auth } from '@clerk/nextjs/server';
// 创建tRPC上下文
// 该函数会在每个请求中被调用
// cache() 会在服务端请求中缓存这个异步函数的结果,避免重复执行
// auth() 获取用户信息
export const createTRPCContext = cache(async () => {
const { userId } = await auth();
return { clerkUserId: userId };
});
- 类型推导 Type Inference
- 不需要手动写类型,它会根据代码上下文,自动判断出变量、函数、返回值的类型
- 编译阶段就做好 “静态安全检查”,避免出现取错值、空值或者数据结构发生改变的状态
// tRPC类型推导
// typeof createTRPCContext 结果:() => Promise<{ clerkUserId: string | null; }>
// ReturnType<typeof createTRPCContext> 获取函数返回类型,是Promise类型
// Awaited<> ts内置的类型工具,可提取Promise的返回值类型
// 结合起来,最终的结果是 { clerkUserId: string | null; }
export type Context = Awaited<ReturnType<typeof createTRPCContext>>;
// 创建tRPC实例
// 将 Context 类型注入到所有 procedure 的上下文中
const t = initTRPC.context<Context>().create({
transformer: superjson,
});
-
Add protectedProcedure & rate limiting
-
t.procedure.use() 使用中间件鉴权
- clerk 与 数据库
users 的数据是同步的,所以都需要验证
- upstash - the Nextjs Example
- 版本信息:"@upstash/redis": "^1.35.3"、"@upstash/ratelimit": "^2.0.6"
// src/lib/redis.ts
import { Redis } from '@upstash/redis';
export const redis = new Redis({
url: process.env.UPSTASH_REDIS_REST_URL,
token: process.env.UPSTASH_REDIS_REST_TOKEN,
});
// src/lib/ratelimit.ts
import { Ratelimit } from '@upstash/ratelimit'
import { redis } from './redis'
export const ratelimit = new Ratelimit({
redis,
limiter: Ratelimit.slidingWindow(10, '10s'), // 10 requests per 10 seconds
})
import { initTRPC, TRPCError } from '@trpc/server';
...
import { auth } from '@clerk/nextjs/server';
import { db } from '@/db';
import { eq } from 'drizzle-orm';
import { users } from '@/db/schema';
import { ratelimit } from '@/lib/ratelimit';
...
...
// 中间件式鉴权
// t.procedure.use() 使用中间件的方式
export const protectedProcedure = t.procedure.use(async function isAuthed(opts) {
const {ctx} = opts;
if (!ctx.clerkUserId) throw new TRPCError({ code: 'UNAUTHORIZED' }); // 自动返回401
const [user] = await db
.select()
.from(users)
.where(eq(users.clerkId, ctx.clerkUserId))
.limit(1);
if (!user) throw new TRPCError({ code: 'UNAUTHORIZED' }); // 自动返回401
const {success} = await ratelimit.limit(user.id);
if(!success) throw new TRPCError({code: 'TOO_MANY_REQUESTS'});
// 请求继续,进入实际的处理函数(resolver)
return opts.next({
ctx: {
...ctx,
user
},
})
})