Function Middleware
Configure authentication and authorization middleware for server functions in your TanStack Start Prisma SaaS application.
Function middleware keeps auth and authorization out of feature handlers while preserving TanStack Start compiler detection. Define each server function with a literal createServerFn(...), then attach tuple middleware from @kit/function-middleware/functions and lower-level gates from @kit/function-middleware/server.
Package Shape
@kit/function-middleware/functionsexports middleware tuples for server functions.@kit/function-middleware/serverexports lower-level middleware and permission gates.- Root
@kit/function-middlewareexports only the tuple middleware surface.
Authenticated Function
import { createServerFn } from '@tanstack/react-start';import { authFunctionMiddleware } from '@kit/function-middleware/functions';export const updateProfileFunction = createServerFn({ method: 'POST' }) .middleware(authFunctionMiddleware) .validator(updateProfileSchema) .handler(async ({ data, context }) => { const { user, session } = context; return updateProfile({ userId: user.id, sessionId: session.id, data }); });authFunctionMiddleware injects context.user and context.session.
Organization Function
import { createServerFn } from '@tanstack/react-start';import { organizationFunctionMiddleware } from '@kit/function-middleware/functions';export const createProjectFunction = createServerFn({ method: 'POST' }) .middleware(organizationFunctionMiddleware) .validator(createProjectSchema) .handler(async ({ data, context }) => { return createProject({ ...data, organizationId: context.organizationId, userId: context.user.id, }); });organizationFunctionMiddleware injects context.user, context.session, context.organizationId, and context.role.
Admin Function
import { createServerFn } from '@tanstack/react-start';import { adminFunctionMiddleware } from '@kit/function-middleware/functions';export const listUsersFunction = createServerFn({ method: 'POST' }) .middleware(adminFunctionMiddleware) .validator(listUsersSchema) .handler(async ({ data, context }) => { return listUsers({ adminId: context.user.id, ...data }); });adminFunctionMiddleware injects context.user and context.session after verifying the user is an admin.
Extra Gates
Layer reusable authorization gates with the native .middleware([...]) API:
import { createServerFn } from '@tanstack/react-start';import { authFunctionMiddleware } from '@kit/function-middleware/functions';import { withFeaturePermission, withMinRole } from '@kit/function-middleware/server';export const deleteProjectFunction = createServerFn({ method: 'POST' }) .middleware([ ...authFunctionMiddleware, withMinRole('owner'), withFeaturePermission({ project: ['delete'] }), ]) .validator(deleteProjectSchema) .handler(async ({ data, context }) => { return deleteProject({ userId: context.user.id, projectId: data.projectId }); });Admin Permissions
import { createServerFn } from '@tanstack/react-start';import { adminFunctionMiddleware } from '@kit/function-middleware/functions';import { withAdminPermission } from '@kit/function-middleware/server';export const banUserFunction = createServerFn({ method: 'POST' }) .middleware([ ...adminFunctionMiddleware, withAdminPermission({ user: ['ban'] }), ]) .validator(banUserSchema) .handler(async ({ data, context }) => { return banUser({ adminId: context.user.id, userId: data.userId }); });Client Calls
Client components should wrap server functions with useServerFn(...) before passing them to TanStack Query:
import { useMutation } from '@tanstack/react-query';import { useServerFn } from '@tanstack/react-start';import { updateProfileFunction } from '../lib/functions/update-profile.functions';export function ProfileForm() { const updateProfile = useServerFn(updateProfileFunction); const mutation = useMutation({ mutationFn: updateProfile, });}Rules
- Keep
createServerFn(...)literal at the function definition site. - Put server functions in
*.functions.tsfiles. - Name exports with the
Functionsuffix. - Use tuple middleware instead of hand-written auth checks.
- Use
useServerFn(...)from client components.