一键导入
server-functions
Use createServerFn for RPC-style functions. Use when writing server functions with validators, middleware chains, or form action handlers.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Use createServerFn for RPC-style functions. Use when writing server functions with validators, middleware chains, or form action handlers.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
Git workflow for branches, commits, and PRs. Use for commit, branch, pr, pull request, conventional, push, feat, fix, chore, merge, rebase
Storybook stories and interaction tests. Use for story, stories, storybook, chromatic, visual test, interaction test, play function, component test
TypeScript and unicorn linting patterns. Use for typescript, array, for-of, reduce, forEach, throw, catch, modern js, es modules, string, number, error handling
Zod v4 schema validation. Use for zod, schema, validation, parse, safeParse, infer, coerce, transform, refine, z.object, z.string, z.email, z.url
Better Auth authentication. Use for auth, login, logout, session, user, signup, register, protect, middleware, password, oauth, social
Drizzle ORM + PostgreSQL database layer. Use for db, database, query, schema, table, migrate, sql, postgres, drizzle, model, relation
| name | server-functions |
| description | Use createServerFn for RPC-style functions. Use when writing server functions with validators, middleware chains, or form action handlers. |
import { createServerFn } from '@tanstack/react-start';
import { z } from 'zod';
import { db } from '@oakoss/database';
const getUsers = createServerFn({ method: 'GET' }).handler(async () => {
return await db.query.users.findMany();
});
const createUser = createServerFn({ method: 'POST' })
.inputValidator(z.object({ name: z.string().min(1), email: z.email() }))
.handler(async ({ data }) => {
const [user] = await db.insert(users).values(data).returning();
return user;
});
| Pattern | Use Case | Method |
|---|---|---|
| Data fetching | Route loaders, queries | GET |
| Form submission | Create/update operations | POST |
| Authenticated action | Protected mutations | POST + auth check |
import { db, eq } from '@oakoss/database';
import { posts } from '@oakoss/database/schema';
const getAllPosts = createServerFn({ method: 'GET' }).handler(async () => {
return await db.query.posts.findMany();
});
const getPost = createServerFn({ method: 'GET' })
.inputValidator(z.object({ id: z.string() }))
.handler(async ({ data }) => {
return await db.query.posts.findFirst({
where: eq(posts.id, data.id),
});
});
export const Route = createFileRoute('/posts/$id')({
loader: async ({ params }) => await getPost({ data: { id: params.id } }),
});
const createPost = createServerFn({ method: 'POST' })
.inputValidator(
z.object({
title: z.string().min(1),
content: z.string(),
}),
)
.handler(async ({ data }) => {
const [post] = await db.insert(posts).values(data).returning();
return post;
});
const handleSubmit = async (formData: FormData) => {
const result = await createPost({
data: { title: formData.get('title'), content: formData.get('content') },
});
};
import { auth } from '@oakoss/auth/server';
import { db, eq } from '@oakoss/database';
import { posts } from '@oakoss/database/schema';
const deletePost = createServerFn({ method: 'POST' })
.inputValidator(z.object({ id: z.string() }))
.handler(async ({ data, request }) => {
// Get session from request headers
const session = await auth.api.getSession({ headers: request.headers });
if (!session) {
return { error: 'Authentication required', code: 'AUTH_REQUIRED' };
}
// Check ownership
const post = await db.query.posts.findFirst({
where: eq(posts.id, data.id),
});
if (post?.authorId !== session.user.id) {
return { error: 'Not authorized', code: 'FORBIDDEN' };
}
await db.delete(posts).where(eq(posts.id, data.id));
return { success: true };
});
Standard error response format:
type ApiResult<T> = { data: T } | { error: string; code: string };
const updateUser = createServerFn({ method: 'POST' })
.inputValidator(schema)
.handler(async ({ data, request }): Promise<ApiResult<User>> => {
try {
const [user] = await db.update(users).set(data).returning();
return { data: user };
} catch (error) {
console.error('updateUser failed:', error);
return { error: 'Update failed', code: 'INTERNAL_ERROR' };
}
});
Access request details:
const serverFn = createServerFn({ method: 'POST' }).handler(
async ({ request }) => {
// Headers
const authHeader = request.headers.get('Authorization');
// URL info
const url = new URL(request.url);
// Cookies (via Better Auth)
const session = await auth.api.getSession({ headers: request.headers });
},
);
| Mistake | Correct Pattern |
|---|---|
| Missing auth check | Always verify session from request headers |
| Throwing instead of returning error | Return { error, code } format |
| Not validating input | Always use .inputValidator(schema) |
| Using deprecated Zod v3 syntax | Use z.email() not z.string().email() |
| Missing error code | Return { error: 'message', code: 'ERROR_CODE' } |
| Not logging server errors | Use console.error() before returning |
| Returning raw DB errors | Catch and return user-friendly messages |
| Using GET for mutations | Use POST for create/update/delete |
database skillauth skilltanstack-start skillcode-reviewer agent