Skip to content
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 77 additions & 0 deletions docs/start/framework/react/guide/middleware.md
Original file line number Diff line number Diff line change
Expand Up @@ -758,3 +758,80 @@ Middleware functionality is tree-shaken based on the environment for each bundle

- On the server, nothing is tree-shaken, so all code used in middleware will be included in the server bundle.
- On the client, all server-specific code is removed from the client bundle. This means any code used in the `server` method is always removed from the client bundle. `data` validation code will also be removed.

## Dynamic Middleware

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rather call it middleware factories

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done, renamed throughout the section.


Static middlewares are created once and reused across routes. Dynamic middleware wraps that creation in a function, allowing it to accept parameters and behave differently depending on the caller's needs. Authorization is a common use case.

**Authentication (Static Base Middleware) Example:**

This middleware validates the session and injects it into `context` for downstream middlewares.

```tsx
// middleware.ts
import { createMiddleware } from '@tanstack/react-start';
import { auth } from './my-auth';

export const authMiddleware = createMiddleware().server(
async ({ next, request }) => {
const session = await auth.getSession({ headers: request.headers });

if (!session) {
throw new Error('Unauthorized');
}

return await next({
context: { session },
});
},
);
```

**Authorization (Dynamic Middleware) Example:**

The middleware validates access based on the dynamic `permissions` parameter, composing with `authMiddleware` so `context.session` is already available.

```tsx
// middleware.ts
import { createMiddleware } from '@tanstack/react-start';
import { auth } from './my-auth';

export const authMiddleware = createMiddleware().server(
//...
);

type Permissions = Record<string, string[]>;

export function authorizationMiddleware(permissions: Permissions) {
return createMiddleware({ type: 'function' })
.middleware([authMiddleware])
.server(async ({ next, context }) => {
const granted = await auth.hasPermission(context.session, permissions);

if (!granted) {
throw new Error('Forbidden');
}

return await next();
});
}
```

**Usage in a Server Function:**

Access requirements are defined per server function, without duplicating any middleware logic.

```tsx
import { createServerFn } from '@tanstack/react-start';
import { authorizationMiddleware } from './middleware';

export const getClients = createServerFn()
.middleware([
authorizationMiddleware({
client: ['read'],
}),
])
.handler(async ({ context }) => {
return { message: 'The user can read clients.' };
});
```