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.

Matching is boundary-aware: router.use('/api', fn) runs for /api and /api/users, but not for /apiv2, because the pathname must equal the prefix or continue with a /.

Route-Specific prefix matching: router.use('/api', fn) matches /api exactly and /api/users on a boundary slash, but skips /apiv2 and /admin because they are not boundary matches of the prefix

Basic Usage

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

typescript
import { 
Router
} from '@neabyte/deserve'
const
router
= new
Router
()
// Runs for paths starting with /api
router
.
use
('/api', async (
ctx
,
next
) => {
console
.
log
(`API request: ${
ctx
.
get
.
method
()} ${
ctx
.
get
.
pathname
()}`)
return await
next
()
}) 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
// Require a bearer token under /api
router
.
use
('/api', async (
ctx
,
next
) => {
const
authHeader
=
ctx
.
get
.
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
// Allow only the admin role under /admin
router
.
use
('/admin', async (
ctx
,
next
) => {
const
userRole
=
ctx
.
get
.
header
('x-user-role')
if (
userRole
!== 'admin') {
return
ctx
.
send
.
text
('Admin access required', {
status
: 403 })
} return await
next
()
})

Public Route Logging

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

Version-Specific Middleware

typescript
// Separate 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
// Auth runs first under /api
router
.
use
('/api', async (
ctx
,
next
) => {
const
authHeader
=
ctx
.
get
.
header
('authorization')
if (!
authHeader
) {
return
ctx
.
send
.
text
('Unauthorized', {
status
: 401 })
} return await
next
()
}) // Logging runs after auth passes
router
.
use
('/api', async (
ctx
,
next
) => {
console
.
log
(`API: ${
ctx
.
get
.
method
()} ${
ctx
.
get
.
pathname
()}`)
return await
next
()
})

Nested Route Patterns

Apply middleware to nested route patterns:

typescript
// Covers every path under /api
router
.
use
('/api', async (
ctx
,
next
) => {
console
.
log
('API request')
return await
next
()
}) // Narrows to /api/users
router
.
use
('/api/users', async (
ctx
,
next
) => {
console
.
log
('User API request')
return await
next
()
}) // Narrows further and checks role
router
.
use
('/api/users/admin', async (
ctx
,
next
) => {
const
role
=
ctx
.
get
.
header
('x-user-role')
if (
role
!== 'admin') {
return
ctx
.
send
.
text
('Admin access required', {
status
: 403 })
} return await
next
()
})

Middleware Execution Order

Middleware runs in the order it is added:

Route-Specific execution for GET /api/users in one chain: the global logger runs, the /api auth runs on a prefix match, the /admin guard is skipped because it does not match without consuming a turn, the /api/users logger runs, then the route handler executes, all in registration order

typescript
// Global runs for every request
router
.
use
(async (
ctx
,
next
) => {
console
.
log
('Global middleware')
return await
next
()
}) // Path middleware runs for /api requests
router
.
use
('/api', async (
ctx
,
next
) => {
console
.
log
('API middleware')
return await
next
()
}) // For /api/users: global, then API, then handler

Released under the MIT License.