Hooks
Introduction
The Movk Nuxt API system exposes 4 extension points via Nuxt runtime hooks, allowing you to extend and customize API behavior without modifying source code.
All hook types are automatically registered into RuntimeNuxtHooks via module declaration; in your editor, nuxtApp.hook('movk:api:') provides full auto-completion.
Available Hooks
movk:api:request
When triggered: After auth token injection, before the request is actually sent.
'movk:api:request': (context: FetchContext) => void | Promise<void>
Typical use cases: Adding extra request headers, request logging, modifying request parameters.
export default defineNuxtPlugin((nuxtApp) => {
nuxtApp.hook('movk:api:request', (context) => {
console.log(`[API] ${context.options.method || 'GET'} ${context.request}`)
})
})
movk:api:response
When triggered: After business status code validation passes and data unwrapping is complete.
'movk:api:response': (
context: FetchContext & { response: FetchResponse<any> }
) => void | Promise<void>
At this point, context.response._data is already the unwrapped business data.
Typical use cases: Global data transformation, response logging, performance analysis.
export default defineNuxtPlugin((nuxtApp) => {
nuxtApp.hook('movk:api:response', (context) => {
analytics.track('api_success', {
url: context.request,
method: context.options.method || 'GET'
})
})
})
movk:api:error
When triggered: On business status code validation failure or HTTP error (4xx/5xx). For 401 errors, this fires after movk:api:unauthorized.
'movk:api:error': (
context: FetchContext & { response: FetchResponse<any> }
) => void | Promise<void>
Typical use cases: Global error reporting (Sentry, etc.), error analytics.
export default defineNuxtPlugin((nuxtApp) => {
nuxtApp.hook('movk:api:error', (context) => {
// Report to Sentry (skip already-handled 401s)
if (context.response.status !== 401) {
Sentry.captureException(new Error(`API Error: ${context.response.status}`), {
extra: {
url: context.request,
data: context.response._data
}
})
}
})
})
movk:api:unauthorized
When triggered: When an HTTP 401 response is received, before movk:api:error.
'movk:api:unauthorized': (
context: FetchContext & { response: FetchResponse<any> },
result: { handled: boolean }
) => void | Promise<void>
Setting result.handled = true skips the default 401 handling (clearing the session and redirecting to the login page).
result.handled = true will prevent the default behavior.Token Refresh Pattern
export default defineNuxtPlugin((nuxtApp) => {
let isRefreshing = false
nuxtApp.hook('movk:api:unauthorized', async (_context, result) => {
if (isRefreshing) return
isRefreshing = true
try {
// Attempt to refresh the token
await $fetch('/api/auth/refresh', { method: 'POST' })
// Refresh session state
const { fetch: fetchSession } = useUserSession()
await fetchSession()
// Mark as handled, skip default 401 behavior
result.handled = true
}
catch {
// Refresh failed — fall through to default behavior (clear session + redirect to login)
}
finally {
isRefreshing = false
}
})
})
Registration
In a Nuxt Plugin (Recommended)
Suitable for global logic that is automatically registered at app startup:
export default defineNuxtPlugin((nuxtApp) => {
nuxtApp.hook('movk:api:request', (context) => {
// Global request handling
})
nuxtApp.hook('movk:api:error', (context) => {
// Global error handling
})
})
In a Component
Suitable for page-scoped logic:
<script setup>
const nuxtApp = useNuxtApp()
// Only active on the current page
const unregister = nuxtApp.hook('movk:api:error', (context) => {
// Page-level error handling
})
onUnmounted(() => {
unregister() // Clean up when the component is destroyed
})
</script>
onUnmounted to avoid memory leaks.Execution Order
Request initiated
│
├── Auth token injection
├── Debug logging
├── movk:api:request <-- user extension point
│
├── Network request sent ─────────────>
│
├── HTTP 2xx response
│ ├── Business success (code in successCodes)
│ │ ├── Data unwrap (extract dataKey)
│ │ ├── movk:api:response <-- user extension point
│ │ └── Success Toast
│ │
│ └── Business failure
│ ├── movk:api:error <-- user extension point
│ ├── Error Toast
│ └── throw ApiError
│
└── HTTP 4xx/5xx response
├── [401] movk:api:unauthorized <-- user extension point
│ └── (if not handled) clear session + redirect to login
├── movk:api:error <-- user extension point
└── Error Toast
TypeScript Support
Hook types are automatically registered via module declaration:
declare module 'nuxt/app' {
interface RuntimeNuxtHooks {
'movk:api:request': (context: FetchContext) => void | Promise<void>
'movk:api:response': (context: FetchContext & { response: FetchResponse<any> }) => void | Promise<void>
'movk:api:error': (context: FetchContext & { response: FetchResponse<any> }) => void | Promise<void>
'movk:api:unauthorized': (
context: FetchContext & { response: FetchResponse<any> },
result: { handled: boolean }
) => void | Promise<void>
}
}
No additional configuration is needed — nuxtApp.hook('movk:api:...') provides full type inference and auto-completion out of the box.
$api
The enhanced $fetch instance created by the api.factory plugin, supporting auth injection, data unwrapping, business code checking and multi-endpoint switching.
useApiFetch
An API request composable built on Nuxt useFetch, providing automatic authentication, business status code checking and Toast notifications.