Skip to content

Route-Specific Middleware

Route-specific middleware applies to specific route patterns, allowing targeted functionality like authentication for API routes or logging for admin routes.

Basic Usage

Apply middleware to specific route patterns using the use() method with a route path:

typescript
// 1. Import Router
import { Router } from '@neabyte/deserve'

// 2. Create router
const router = new Router()

// 3. Apply middleware for paths starting with /api (prefix match)
router.use('/api', async (ctx, next) => {
  console.log(`API request: ${ctx.request.method} ${ctx.url}`)
  return await next()
})

// 4. Start server
await router.serve(8000)

Route Pattern Matching

Middleware applies to routes that start with the specified pattern:

typescript
// Applies to /api/* routes
router.use('/api', middleware)

// Applies to /api/users/* routes
router.use('/api/users', middleware)

// Applies to /admin/* routes
router.use('/admin', middleware)

Common Route-Specific Patterns

API Authentication

typescript
// 1. Auth only for /api
router.use('/api', async (ctx, next) => {
  const authHeader = ctx.header('authorization')
  if (!authHeader) {
    return ctx.send.text('API requires authentication', { status: 401 })
  }
  const token = authHeader.replace('Bearer ', '')
  if (!isValidToken(token)) {
    return ctx.send.text('Invalid token', { status: 401 })
  }
  return await next()
})

Admin Authorization

typescript
// 1. Check admin role for /admin
router.use('/admin', async (ctx, next) => {
  const userRole = ctx.header('x-user-role')
  if (userRole !== 'admin') {
    return ctx.send.text('Admin access required', { status: 403 })
  }
  return await next()
})

Public Route Logging

typescript
// 1. Log access to /public
router.use('/public', async (ctx, next) => {
  console.log(`Public access: ${ctx.request.method} ${ctx.url}`)
  return await next()
})

Version-Specific Middleware

typescript
// 1. Middleware per API version
router.use('/api/v1', async (ctx, next) => {
  console.log('Legacy API v1 request')
  return await next()
})

router.use('/api/v2', async (ctx, next) => {
  console.log('Modern API v2 request')
  return await next()
})

Multiple Route-Specific Middleware

Apply multiple middleware to the same route pattern:

typescript
// 1. Auth first for /api
router.use('/api', async (ctx, next) => {
  const authHeader = ctx.header('authorization')
  if (!authHeader) {
    return ctx.send.text('Unauthorized', { status: 401 })
  }
  return await next()
})

// 2. Then logging (runs after auth ok)
router.use('/api', async (ctx, next) => {
  console.log(`API: ${ctx.request.method} ${ctx.url}`)
  return await next()
})

Nested Route Patterns

Apply middleware to nested route patterns:

typescript
// 1. /api → all paths under /api
router.use('/api', async (ctx, next) => {
  console.log('API request')
  return await next()
})

// 2. /api/users → more specific
router.use('/api/users', async (ctx, next) => {
  console.log('User API request')
  return await next()
})

// 3. /api/users/admin → check role
router.use('/api/users/admin', async (ctx, next) => {
  const role = ctx.header('x-user-role')
  if (role !== 'admin') {
    return ctx.send.text('Admin access required', { status: 403 })
  }
  return await next()
})

Middleware Execution Order

Middleware executes in the order it's added:

typescript
// 1. Global: runs for every request
router.use(async (ctx, next) => {
  console.log('Global middleware')
  return await next()
})

// 2. Path /api: runs if path starts with /api
router.use('/api', async (ctx, next) => {
  console.log('API middleware')
  return await next()
})

// Execution order for request to /api/users:
// 1. Global middleware (added first)
// 2. API middleware (route-specific, added second)
// 3. Route handler

Released under the MIT License.