| name | vibe-any-dev |
| description | Full-stack development guide for the VibeAny project. Covers creating pages, API routes, database tables, admin panels, landing page sections, i18n content, and server functions. Use when building any new feature, page, API endpoint, or modifying the landing page in this TanStack Start + React + Drizzle + Intlayer codebase. |
VibeAny 全栈开发指南
需求路由表
根据需求类型,阅读对应的 reference 文件:
全局规则
以下规则适用于所有开发场景,任何 reference 中的代码都必须遵守。
Import 别名
所有项目内 import 使用 @/ 前缀:
import { db } from "@/db"
import { Resp } from "@/shared/lib/tools/response"
import { cn } from "@/shared/lib/utils"
className 拼接
禁止模板字面量拼接,使用 cn() 函数:
import { cn } from "@/shared/lib/utils"
<div className={cn("base-class", isActive && "active-class")} />
<div className={`base-class ${isActive ? "active-class" : ""}`} />
路由链接
禁止使用 TanStack Router 的 <Link>,使用 LocalizedLink:
import { LocalizedLink } from "@/shared/components/locale/localized-link"
<LocalizedLink to="/chat">Chat</LocalizedLink>
编程式跳转:
import { useLocalizedNavigate } from "@/shared/hooks/use-localized-navigate"
const navigate = useLocalizedNavigate()
navigate({ to: "/chat" })
图片
使用 unpic 的 Image 组件:
import { Image } from "@unpic/react"
<Image src="/images/logo.png" alt="Logo" width={100} height={100} />
请求与响应
本项目有一套统一的请求/响应/错误处理链路:
后端 Resp 返回 → ApiResponse 格式 → 前端 http 自动解包 data → 出错时 errorEmitter → ErrorToaster 展示 i18n 弹窗
后端:Resp 响应工具(API 路由专用)
import { Resp } from "@/shared/lib/tools/response"
Resp.success(data)
Resp.error("Unauthorized", 401, "FORBIDDEN")
Resp.error("Something failed", 500)
错误码(ErrorCode)
定义在 src/config/locale/error.content.ts,前端 ErrorToaster 会根据错误码自动展示对应的多语言 toast:
UNAUTHORIZED
FORBIDDEN
NOT_FOUND
VALIDATION_FAILED
NETWORK_ERROR
UNKNOWN_ERROR
新增错误码:在 error.content.ts 中添加条目,类型自动推导。
前端:http 客户端
import { http } from "@/shared/lib/tools/http-client"
http 基于 ofetch,会自动解包 ApiResponse.data,返回的直接就是业务数据:
const items = await http<MyItem[]>("/api/admin/tags")
await http("/api/admin/tags", { method: "POST", body: { name: "test" } })
await http("/api/admin/tags", { method: "PUT", body: { id, name: "new" } })
await http(`/api/admin/tags?id=${id}`, { method: "DELETE" })
错误处理:当后端返回 error 字段时,http 自动抛出 HttpError 并通过 errorEmitter 触发全局 toast,前端代码无需手动处理 toast。
可选参数:
const data = await http<MyData>("/api/me", { requireAuth: true })
try {
await http("/api/check", { silent: true })
} catch (e) {
}
前后端配合模式
POST: async ({ request }) => {
try {
const body = await request.json()
const data = createSchema.parse(body)
const [created] = await db.insert(example).values(data).returning()
return Resp.success(created)
} catch (error) {
if (error instanceof z.ZodError) {
return Resp.error(`Invalid: ${error.issues.map(i => i.message).join(", ")}`, 400)
}
return Resp.error("Failed", 500)
}
}
const mutation = useMutation({
mutationFn: (data: FormData) =>
http("/api/admin/example", { method: "POST", body: data }),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ["admin", "example"] })
toast.success("Created")
},
})
Zod 导入
本项目使用 zod v4,导入路径为:
import { z } from "zod/v4"
数据库迁移
禁止未经用户允许执行 drizzle-kit push、drizzle-kit migrate 等迁移命令。创建完 schema 后提醒用户自行执行迁移。
aria-label
保持英文,不需要多语言。
类型推导
数据库表中有的类型,应在 src/shared/types/ 中从 schema 推导,不要手写重复类型:
import type { user } from "@/db/auth.schema"
export type User = typeof user.$inferSelect