Files
policy-ui/.claude/skills/nuxt/references/middleware-plugins.md
HaimKortovich a2eb1f3789 Add nuxt-skills and update auto quotes to use new policy API structure
- Add nuxt-skills (vue, nuxt, nuxt-ui) to .claude/skills/
- Create useCustomerSelection() composable for managing insured/buyer selection
- Create usePolicyApi() composable for policy API operations
- Update auto quote components to use insured/buyer instead of client
- Update vehicle fields: remove valorVehiculo, add market_value, requested_value, rc_limits
- Make chassis_number and engine_number optional
- Update auto quote types and composables to match new API structure
- Update auto quote page to submit to policy API with new structure
2026-04-27 14:56:53 -05:00

6.0 KiB

Nuxt Middleware & Plugins

When to Use

Working with middleware/ or plugins/ directories, route guards, app extensions.

Route Middleware

Route middleware runs before navigation. Used for auth checks, redirects, logging.

Global Middleware

Runs on every route change. REQUIRED: Use .global.ts suffix:

// middleware/auth.global.ts
export default defineNuxtRouteMiddleware((to, from) => {
  const auth = useAuthStore()

  if (to.meta.requiresAuth && !auth.isAuthenticated) {
    return navigateTo('/login')
  }
})

Without .global.ts suffix, middleware is named (not global).

Red Flags - Stop and Check Skill

If you're thinking any of these, STOP and re-read this skill:

  • "Suffix doesn't matter, it's about where I put it"
  • "I'll redirect() instead of return navigateTo()"
  • "I remember Nuxt 3 middleware patterns"
  • "Export default function is simpler"

All of these mean: You're using outdated patterns. Use Nuxt 4 patterns instead.

Named Middleware

Runs only when explicitly applied. No .global suffix:

// middleware/admin.ts
export default defineNuxtRouteMiddleware((to, from) => {
  const auth = useAuthStore()

  if (!auth.isAdmin) {
    return navigateTo('/')
  }
})

Apply in page:

<script setup lang="ts">
definePageMeta({
  middleware: ['admin']
})
</script>

Middleware Return Values

export default defineNuxtRouteMiddleware((to, from) => {
  // Allow navigation
  return

  // Redirect
  return navigateTo('/login')

  // Abort navigation
  return abortNavigation()

  // Abort with error
  return abortNavigation('Not authorized')
})

Middleware Order

  1. Global middleware (alphabetical by filename)
  2. Layout middleware (if layout defines middleware)
  3. Page middleware (defined in definePageMeta)

Plugins

Plugins extend Vue app with global functionality. Run during app initialization.

Basic Plugin

// plugins/my-plugin.ts
export default defineNuxtPlugin((nuxtApp) => {
  return {
    provide: {
      hello: (name: string) => `Hello ${name}!`
    }
  }
})

Use in components:

<script setup lang="ts">
const { $hello } = useNuxtApp()
console.log($hello('World')) // "Hello World!"
</script>

Plugin with Vue Plugin

import type { PluginOptions } from 'vue-toastification'
// plugins/toast.client.ts
import Toast from 'vue-toastification'
import 'vue-toastification/dist/index.css'

export default defineNuxtPlugin((nuxtApp) => {
  nuxtApp.vueApp.use(Toast, {
    position: 'top-right',
    timeout: 3000
  } as PluginOptions)
})

Plugin with Hooks

// plugins/init.ts
export default defineNuxtPlugin((nuxtApp) => {
  nuxtApp.hook('app:created', () => {
    console.log('App created')
  })

  nuxtApp.hook('page:finish', () => {
    console.log('Page finished loading')
  })
})

Client-Only or Server-Only

Use file suffix:

  • .client.ts - runs only on client
  • .server.ts - runs only on server
// plugins/analytics.client.ts
export default defineNuxtPlugin(() => {
  // Only runs in browser
  if (window.analytics) {
    window.analytics.init()
  }
})

Plugin Order

Use numeric prefix for execution order:

plugins/
├── 01.first.ts
├── 02.second.ts
└── 03.third.ts

Async Plugins

// plugins/api.ts
export default defineNuxtPlugin(async (nuxtApp) => {
  const config = await fetch('/api/config').then(r => r.json())

  return {
    provide: {
      config
    }
  }
})

Best Practices

Middleware:

  • Return navigation or nothing - don't mutate state heavily
  • Keep logic minimal - delegate to composables/stores
  • Use for guards & redirects only
  • Check meta properly - to.meta.requiresAuth
  • Global = .global.ts suffix required

Plugins:

  • Use for app-wide functionality only
  • Provide via provide for type safety
  • Consider client/server context - use .client/.server
  • Minimize work in plugin initialization
  • Use hooks for lifecycle events

Common Mistakes

Wrong Right
export default function({ route }) export default defineNuxtRouteMiddleware((to, from) => {})
Mutate route object Return navigateTo() or nothing
middleware/auth.ts (not global) middleware/auth.global.ts (global)
redirect('/login') return navigateTo('/login')
Plugin without defineNuxtPlugin Wrap in defineNuxtPlugin()

Middleware Example: Auth

// middleware/auth.global.ts
export default defineNuxtRouteMiddleware((to, from) => {
  const auth = useAuthStore()

  // Public routes
  const publicRoutes = ['/', '/login', '/register']
  if (publicRoutes.includes(to.path)) {
    return
  }

  // Check auth
  if (!auth.isAuthenticated) {
    return navigateTo('/login')
  }

  // Check role
  if (to.meta.requiresAdmin && !auth.isAdmin) {
    return abortNavigation('Access denied')
  }
})

Plugin Example: API Client

// plugins/api.ts
export default defineNuxtPlugin((nuxtApp) => {
  const config = useRuntimeConfig()

  const api = $fetch.create({
    baseURL: config.public.apiBase,
    onRequest({ request, options }) {
      const auth = useAuthStore()
      if (auth.token) {
        options.headers = {
          ...options.headers,
          Authorization: `Bearer ${auth.token}`
        }
      }
    },
    onResponseError({ response }) {
      if (response.status === 401) {
        navigateTo('/login')
      }
    }
  })

  return {
    provide: {
      api
    }
  }
})

Resources