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/functions exports middleware tuples for server functions.
  • @kit/function-middleware/server exports lower-level middleware and permission gates.
  • Root @kit/function-middleware exports 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

  1. Keep createServerFn(...) literal at the function definition site.
  2. Put server functions in *.functions.ts files.
  3. Name exports with the Function suffix.
  4. Use tuple middleware instead of hand-written auth checks.
  5. Use useServerFn(...) from client components.