Skip to content

Commit 64dee75

Browse files
docs(start): add dynamic middleware section with authorization example (#6815)
* docs(start): add dynamic middleware section with authorization example * docs(start): rename dynamic middleware to middleware factories * ci: apply automated fixes * docs(start): fix authMiddleware snippet in authorization example under middleware factories * ci: apply automated fixes --------- Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
1 parent 861f319 commit 64dee75

1 file changed

Lines changed: 79 additions & 0 deletions

File tree

docs/start/framework/react/guide/middleware.md

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -758,3 +758,82 @@ Middleware functionality is tree-shaken based on the environment for each bundle
758758

759759
- On the server, nothing is tree-shaken, so all code used in middleware will be included in the server bundle.
760760
- 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.
761+
762+
## Middleware Factories
763+
764+
Static middlewares are created once and reused across routes. A middleware factory 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.
765+
766+
**Authentication (Static Base Middleware) Example:**
767+
768+
This middleware validates the session and injects it into `context` for downstream middlewares.
769+
770+
```tsx
771+
// middleware.ts
772+
import { createMiddleware } from '@tanstack/react-start'
773+
import { auth } from './my-auth'
774+
775+
export const authMiddleware = createMiddleware().server(
776+
async ({ next, request }) => {
777+
const session = await auth.getSession({ headers: request.headers })
778+
779+
if (!session) {
780+
throw new Error('Unauthorized')
781+
}
782+
783+
return await next({
784+
context: { session },
785+
})
786+
},
787+
)
788+
```
789+
790+
**Authorization (Middleware Factory) Example:**
791+
792+
The middleware validates access based on the dynamic `permissions` parameter, composing with `authMiddleware` so `context.session` is already available.
793+
794+
```tsx
795+
// middleware.ts
796+
import { createMiddleware } from '@tanstack/react-start'
797+
import { auth } from './my-auth'
798+
799+
export const authMiddleware = createMiddleware().server(
800+
async ({ next, request }) => {
801+
// ... (implementation from authentication example above)
802+
},
803+
)
804+
805+
type Permissions = Record<string, string[]>
806+
807+
export function authorizationMiddleware(permissions: Permissions) {
808+
return createMiddleware({ type: 'function' })
809+
.middleware([authMiddleware])
810+
.server(async ({ next, context }) => {
811+
const granted = await auth.hasPermission(context.session, permissions)
812+
813+
if (!granted) {
814+
throw new Error('Forbidden')
815+
}
816+
817+
return await next()
818+
})
819+
}
820+
```
821+
822+
**Usage in a Server Function:**
823+
824+
Access requirements are defined per server function, without duplicating any middleware logic.
825+
826+
```tsx
827+
import { createServerFn } from '@tanstack/react-start'
828+
import { authorizationMiddleware } from './middleware'
829+
830+
export const getClients = createServerFn()
831+
.middleware([
832+
authorizationMiddleware({
833+
client: ['read'],
834+
}),
835+
])
836+
.handler(async ({ context }) => {
837+
return { message: 'The user can read clients.' }
838+
})
839+
```

0 commit comments

Comments
 (0)