Middleware
Kimesh provides a powerful middleware system for route guards, authentication, analytics, and more. Middleware runs before route navigation completes.
Overview
Middleware can:
- Block navigation and redirect users
- Add data to navigation context
- Run analytics or logging
- Validate permissions
Defining Middleware
File-Based Middleware
Create middleware files in src/middleware/:
src/middleware/
├── auth.global.ts # Runs on every navigation
├── admin.ts # Named middleware (lazy-loaded)
└── 01.analytics.global.ts # Priority-ordered globalGlobal Middleware (.global.ts suffix):
// src/middleware/auth.global.ts
import { defineKimeshMiddleware } from '@kimesh/router-runtime'
export default defineKimeshMiddleware((to, from, context) => {
const { user } = context.app
// Allow public routes
if (to.meta.public) return
// Redirect to login if not authenticated
if (!user.isLoggedIn) {
return { path: '/login', query: { redirect: to.fullPath } }
}
})Named Middleware (loaded on-demand):
// src/middleware/admin.ts
import { defineKimeshMiddleware } from '@kimesh/router-runtime'
export default defineKimeshMiddleware((to, from, context) => {
const { user } = context.app
if (!user.roles.includes('admin')) {
return { path: '/unauthorized' }
}
})Middleware in Routes
Apply middleware to specific routes:
// routes/admin/index.vue
import { createFileRoute } from '@kimesh/router-runtime'
export const Route = createFileRoute('/admin')({
// Single named middleware
middleware: 'admin',
// Multiple middleware
middleware: ['auth', 'admin'],
// Inline middleware
middleware: (to, from, context) => {
if (!context.app.user.isAdmin) {
return { path: '/' }
}
},
// Mixed
middleware: [
'auth',
(to, from, ctx) => {
console.log('Navigating to admin')
},
],
})Middleware Context
interface MiddlewareContext {
to: RouteLocationNormalized // Destination route
from: RouteLocationNormalized // Source route
router: Router // Vue Router instance
app: KimeshAppContext // Kimesh app context
data: Record<string, unknown> // Shared data between middleware
}Access your custom context:
export default defineKimeshMiddleware((to, from, context) => {
// Access queryClient
const { queryClient } = context.app
// Access custom injections from plugins
const { myService } = context.app
})Return Values
Middleware can return different values:
// Allow navigation (implicit)
return
return undefined
// Block navigation
return false
// Redirect to path
return { path: '/login' }
// Redirect with options
return {
path: '/login',
query: { redirect: to.fullPath },
replace: true,
}
// Redirect by name
return { name: 'login', params: { ... } }Priority and Ordering
Global Middleware Order
Use numeric prefixes to control execution order:
src/middleware/
├── 01.setup.global.ts # Runs first (priority 1)
├── 02.auth.global.ts # Runs second (priority 2)
├── 10.analytics.global.ts # Runs later (priority 10)
└── logger.global.ts # No priority = alphabeticalLower priority numbers run first.
Execution Flow
- Global middleware (by priority, then alphabetical)
- Route middleware (in array order)
beforeLoadhook- Route loader
Helpers
navigateTo
Redirect within middleware:
import { defineKimeshMiddleware, navigateTo } from '@kimesh/router-runtime'
export default defineKimeshMiddleware((to, from, context) => {
if (needsRedirect) {
return navigateTo('/new-path', { replace: true })
}
})abortNavigation
Block navigation without redirect:
import { defineKimeshMiddleware, abortNavigation } from '@kimesh/router-runtime'
export default defineKimeshMiddleware((to, from, context) => {
if (shouldBlock) {
abortNavigation()
return false
}
})Composables
useNavigationMiddleware
Register middleware from components:
import { useNavigationMiddleware } from '@kimesh/router-runtime'
// In component setup
useNavigationMiddleware((context) => {
console.log(`Navigating from ${context.from.path} to ${context.to.path}`)
})useNavigationGuard
Simpler guard registration:
import { useNavigationGuard } from '@kimesh/router-runtime'
useNavigationGuard((context) => {
if (hasUnsavedChanges.value) {
return confirm('Discard changes?') ? undefined : false
}
})useAfterNavigation
Run code after navigation completes:
import { useAfterNavigation } from '@kimesh/router-runtime'
useAfterNavigation((context) => {
// Track page view
analytics.track('page_view', { path: context.to.path })
})Layer Middleware
Middleware from layers is merged with app middleware:
- Same-name middleware: app version overrides layer version
- Priority ordering applies across all sources
- Layer middleware can be disabled by defining empty middleware with same name
Typed Middleware
For routes with params, use typed middleware:
import type { TypedRouteMiddleware } from '@kimesh/router-runtime'
type PostParams = { postId: string }
export const postMiddleware: TypedRouteMiddleware<PostParams> = (to, from, ctx) => {
// to.params.postId is typed as string
console.log(`Viewing post: ${to.params.postId}`)
}Generated Types
Kimesh generates middleware types in .kimesh/middleware.types.d.ts:
export const middlewareNames = ['auth', 'admin', 'analytics'] as const
export type MiddlewareName = (typeof middlewareNames)[number]Best Practices
- Use global middleware sparingly - Only for truly global concerns like auth
- Name middleware clearly -
auth,admin,analyticsnotcheck1,mw2 - Keep middleware focused - One responsibility per middleware
- Use priority for dependencies - If middleware B depends on A, give A lower priority
- Colocate route-specific guards - Use inline middleware for route-specific logic