# Function Middleware

> Configure authentication and authorization middleware for server functions in your TanStack Start Prisma SaaS application.

*Canonical: https://makerkit.dev/docs/tanstack-prisma/development-guide/function-middleware*

---

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

```typescript
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

```typescript
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

```typescript
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:

```typescript
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

```typescript
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:

```typescript
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.
