> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/TanStack/router/llms.txt
> Use this file to discover all available pages before exploring further.

# Middleware

# Middleware

Middleware in TanStack Start allows you to intercept and modify requests, responses, and context at both the request and function levels. Use middleware for authentication, logging, error handling, and more.

## Types of Middleware

TanStack Start supports two types of middleware:

1. **Request Middleware** - Runs for all requests (pages and API routes)
2. **Function Middleware** - Runs for specific server functions

## Request Middleware

Request middleware runs on every request before rendering or handling:

```tsx theme={null}
import { createMiddleware } from '@tanstack/react-start'

const authMiddleware = createMiddleware().server(async ({ request, next }) => {
  const session = await getSession(request)
  
  if (!session) {
    throw redirect({ to: '/login' })
  }

  return next({
    context: {
      user: session.user,
    },
  })
})

export const Route = createFileRoute('/dashboard')({
  server: {
    middleware: [authMiddleware],
  },
  loader: async ({ context }) => {
    // context.user is available here
    return { user: context.user }
  },
})
```

## Function Middleware

Function middleware runs specifically for server functions:

```tsx theme={null}
import { createMiddleware, createServerFn } from '@tanstack/react-start'

const loggingMiddleware = createMiddleware({ type: 'function' })
  .client(async ({ next }) => {
    const startTime = Date.now()
    const result = await next()
    console.log(`Request took ${Date.now() - startTime}ms`)
    return result
  })
  .server(async ({ next, serverFnMeta }) => {
    console.log(`Executing function: ${serverFnMeta.name}`)
    return await next()
  })

const fetchData = createServerFn({ method: 'GET' })
  .middleware([loggingMiddleware])
  .handler(async () => {
    return { data: 'Hello' }
  })
```

## Creating Middleware

### Basic Middleware

```tsx theme={null}
import { createMiddleware } from '@tanstack/react-start'

const simpleMiddleware = createMiddleware().server(async ({ request, next }) => {
  console.log('Processing request:', request.url)
  const result = await next()
  console.log('Request complete')
  return result
})
```

### Client + Server Middleware

```tsx theme={null}
const timingMiddleware = createMiddleware({ type: 'function' })
  .client(async ({ next }) => {
    const clientTime = new Date()
    
    return next({
      sendContext: {
        clientTime,
      },
    })
  })
  .server(async ({ next, context }) => {
    const serverTime = new Date()
    const duration = serverTime.getTime() - context.clientTime.getTime()
    
    return next({
      sendContext: {
        serverTime,
        duration,
      },
    })
  })
```

## Middleware Context

### Adding Context

Middleware can add data to the context:

```tsx theme={null}
const userMiddleware = createMiddleware().server(async ({ request, next }) => {
  const userId = request.headers.get('x-user-id')
  const user = await db.users.findById(userId)
  
  return next({
    context: {
      user,
      isAdmin: user?.role === 'admin',
    },
  })
})
```

### Accessing Context

Access context in route handlers and server functions:

```tsx theme={null}
export const Route = createFileRoute('/admin')({
  server: {
    middleware: [userMiddleware],
  },
  loader: async ({ context }) => {
    // context.user and context.isAdmin available
    if (!context.isAdmin) {
      throw redirect({ to: '/' })
    }
    
    return await getAdminData()
  },
})
```

### Send Context

Send data back to the client:

```tsx theme={null}
const dataMiddleware = createMiddleware({ type: 'function' })
  .server(async ({ next }) => {
    const serverData = await getServerData()
    
    return next({
      sendContext: {
        timestamp: Date.now(),
        serverData,
      },
    })
  })
```

Access send context on the client:

```tsx theme={null}
const fetchData = createServerFn({ method: 'GET' })
  .middleware([dataMiddleware])
  .handler(async () => {
    return { result: 'data' }
  })

// In component:
const result = await fetchData()
// result.context.timestamp and result.context.serverData available
```

## Common Middleware Patterns

### Authentication

```tsx theme={null}
const requireAuth = createMiddleware().server(async ({ request, next }) => {
  const token = request.headers.get('Authorization')?.split(' ')[1]
  
  if (!token) {
    throw new Response('Unauthorized', { status: 401 })
  }
  
  try {
    const user = await verifyToken(token)
    return next({ context: { user } })
  } catch (error) {
    throw new Response('Invalid token', { status: 401 })
  }
})
```

### Rate Limiting

```tsx theme={null}
const rateLimitCache = new Map<string, number[]>()

const rateLimit = createMiddleware().server(async ({ request, next }) => {
  const ip = request.headers.get('x-forwarded-for') || 'unknown'
  const now = Date.now()
  const windowMs = 60 * 1000 // 1 minute
  const maxRequests = 100
  
  const requests = rateLimitCache.get(ip) || []
  const recentRequests = requests.filter((time) => now - time < windowMs)
  
  if (recentRequests.length >= maxRequests) {
    throw new Response('Too many requests', { status: 429 })
  }
  
  recentRequests.push(now)
  rateLimitCache.set(ip, recentRequests)
  
  return next()
})
```

### Request Logging

```tsx theme={null}
const logger = createMiddleware().server(async ({ request, next }) => {
  const start = Date.now()
  const { pathname } = new URL(request.url)
  
  console.log(`→ ${request.method} ${pathname}`)
  
  try {
    const result = await next()
    const duration = Date.now() - start
    console.log(`← ${request.method} ${pathname} ${duration}ms`)
    return result
  } catch (error) {
    const duration = Date.now() - start
    console.error(`✗ ${request.method} ${pathname} ${duration}ms`, error)
    throw error
  }
})
```

### CORS Headers

```tsx theme={null}
const cors = createMiddleware().server(async ({ request, next }) => {
  const origin = request.headers.get('origin')
  const result = await next()
  
  if (origin) {
    result.response.headers.set('Access-Control-Allow-Origin', origin)
    result.response.headers.set('Access-Control-Allow-Methods', 'GET, POST, OPTIONS')
    result.response.headers.set('Access-Control-Allow-Headers', 'Content-Type')
  }
  
  return result
})
```

### Error Handling

```tsx theme={null}
const errorHandler = createMiddleware().server(async ({ next }) => {
  try {
    return await next()
  } catch (error) {
    console.error('Middleware error:', error)
    
    if (error instanceof DatabaseError) {
      throw new Response('Database error', { status: 503 })
    }
    
    if (error instanceof ValidationError) {
      throw new Response(JSON.stringify(error.errors), { 
        status: 400,
        headers: { 'Content-Type': 'application/json' },
      })
    }
    
    throw error
  }
})
```

## Composing Middleware

### Middleware Chains

Chain multiple middleware together:

```tsx theme={null}
const apiMiddleware = createMiddleware()
  .middleware([logger, cors, rateLimit, errorHandler])
  .server(async ({ next }) => {
    // All parent middleware runs before this
    return next()
  })
```

### Reusable Middleware

Create reusable middleware for common patterns:

```tsx theme={null}
// middleware/auth.ts
export const requireRole = (role: string) => {
  return createMiddleware().server(async ({ context, next }) => {
    if (context.user?.role !== role) {
      throw new Response('Forbidden', { status: 403 })
    }
    return next()
  })
}

// In routes:
import { requireRole } from '~/middleware/auth'

export const Route = createFileRoute('/admin')({
  server: {
    middleware: [requireAuth, requireRole('admin')],
  },
})
```

## API Route Middleware

Use middleware with API routes:

```tsx theme={null}
import { createFileRoute } from '@tanstack/react-router'
import { createMiddleware } from '@tanstack/react-start'

const apiLogger = createMiddleware().server(async ({ request, next }) => {
  console.log(`API ${request.method} ${request.url}`)
  return next()
})

const validateApiKey = createMiddleware().server(async ({ request, next }) => {
  const apiKey = request.headers.get('x-api-key')
  
  if (!apiKey || !await isValidApiKey(apiKey)) {
    throw new Response('Invalid API key', { status: 401 })
  }
  
  return next()
})

export const Route = createFileRoute('/api/data')({
  server: {
    middleware: [apiLogger, validateApiKey],
    handlers: {
      GET: async ({ request }) => {
        const data = await fetchData()
        return Response.json(data)
      },
    },
  },
})
```

## Global Middleware

Apply middleware to all routes using the Start configuration:

```tsx theme={null}
// app.config.ts
import { createStart } from '@tanstack/react-start'
import { logger, cors } from '~/middleware'

export default createStart({
  requestMiddleware: [logger, cors],
  functionMiddleware: [timingMiddleware],
})
```

## Input Validation Middleware

Validate inputs in middleware:

```tsx theme={null}
import { z } from 'zod'

const validateInput = createMiddleware({ type: 'function' })
  .inputValidator(
    z.object({
      id: z.string().uuid(),
      name: z.string().min(1),
    })
  )
  .server(async ({ data, next }) => {
    // data is validated and typed
    console.log('Valid data:', data)
    return next()
  })
```

## Middleware Execution Order

Middleware executes in order:

```tsx theme={null}
const first = createMiddleware().server(async ({ next }) => {
  console.log('1: before')
  const result = await next()
  console.log('1: after')
  return result
})

const second = createMiddleware().server(async ({ next }) => {
  console.log('2: before')
  const result = await next()
  console.log('2: after')
  return result
})

const fn = createServerFn({ method: 'GET' })
  .middleware([first, second])
  .handler(async () => {
    console.log('handler')
    return 'done'
  })

// Output:
// 1: before
// 2: before
// handler
// 2: after
// 1: after
```

## Best Practices

1. **Keep Middleware Focused**
   * Each middleware should do one thing well
   * Compose multiple middleware for complex scenarios

2. **Handle Errors Properly**
   * Always catch and handle errors in middleware
   * Provide meaningful error responses

3. **Be Careful with Context**
   * Only add necessary data to context
   * Avoid large objects that need serialization

4. **Order Matters**
   * Place authentication/validation middleware first
   * Put logging/monitoring middleware early
   * Error handlers should be at the start of the chain

5. **Type Your Context**
   * Use TypeScript to type context additions
   * Leverage type inference for better DX

6. **Performance Considerations**
   * Avoid expensive operations in middleware
   * Cache results when possible
   * Use async operations wisely

7. **Security First**
   * Validate all inputs
   * Sanitize data before adding to context
   * Use proper authentication and authorization

## Next Steps

* Learn about [Server Functions](./server-functions)
* Explore [API Routes](./api-routes)
* See [Data Fetching](./data-fetching) strategies
