---
title: "Hooks"
description: "Extend API system behavior via Nuxt runtime hooks — global request interception, error handling and custom 401 logic."
canonical_url: "https://nuxt.mhaibaraai.cn/en/docs/api/hooks"
---
# Hooks

> Extend API system behavior via Nuxt runtime hooks — global request interception, error handling and custom 401 logic.

## 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.

```ts
'movk:api:request': (context: FetchContext) => void | Promise<void>
```

Typical use cases: Adding extra request headers, request logging, modifying request parameters.

```ts [plugins/api-logger.ts]
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.

```ts
'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.

```ts [plugins/api-analytics.ts]
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`.

```ts
'movk:api:error': (
  context: FetchContext & { response: FetchResponse<any> }
) => void | Promise<void>
```

Typical use cases: Global error reporting (Sentry, etc.), error analytics.

```ts [plugins/error-reporter.ts]
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`.

```ts
'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).

> [!WARNING]
> 
> If multiple handlers listen to this hook, any handler setting 
> 
> result.handled = true
> 
>  will prevent the default behavior.

#### Token Refresh Pattern

```ts [plugins/token-refresh.ts]
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:

```ts [plugins/api-hooks.ts]
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:

```vue
<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>
```

> [!NOTE]
> 
> When registering hooks inside a component, always call the returned unregister function in 
> 
> onUnmounted
> 
>  to avoid memory leaks.

## Execution Order

```text
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:

```ts [src/runtime/types/module.ts]
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.


## Sitemap

See the full [sitemap](https://nuxt.mhaibaraai.cn/sitemap.md) for all pages.
