Skip to content

useApi & useOrgApi

Reactive composables for API calls in component setup. Wrappers around Nuxt's useFetch with type safety.

When to Use

  • ✅ Loading data on component mount
  • ✅ Data that needs to be reactive in the template
  • ✅ Organization-specific data (use useOrgApi)
  • ❌ Form submissions (use $apiFetch instead)
  • ❌ Event handlers (use $apiFetch instead)

Basic Usage

vue
<script setup>
// General API call
const { data, pending, error } = useApi('/api/v1/auth/me', {
  lazy: true  // ← Always use this for better UX
})

// Organization-scoped (auto-refreshes when user switches org)
const { data: settings } = useOrgApi('/api/organizations/settings', {
  lazy: true
})
</script>

<template>
  <div v-if="pending">Loading...</div>
  <div v-else-if="error">{{ error.message }}</div>
  <div v-else>{{ data }}</div>
</template>

Type Safety

All paths, query params, and responses are fully typed from OpenAPI:

typescript
// ✅ Full autocomplete and type checking
const { data } = useApi('/api/v1/auth/me', { 
  query: { ... }  // TypeScript validates these
})

// data.value is typed - you get autocomplete
console.log(data.value?.name)

useOrgApi Special Behavior

The key difference: useOrgApi concatenates the organization ID into the cache key.

typescript
// When user switches from org123 → org456:
const { data } = useOrgApi('/api/organizations/settings')
// ✅ Automatically refetches for new org
// ✅ No manual refresh needed

When to use useOrgApi:

  • Almost everything under /hr/:orgId routes
  • Organization settings, employees, payroll, time-off, etc.
  • Any data that should refresh when user switches organizations

Common Patterns

Pagination

vue
<script setup>
const page = ref(1)

const { data } = useOrgApi('/api/organizations/employees', {
  query: computed(() => ({ page: page.value, pageSize: 10 })),
  lazy: true
})
</script>

Reactive Path Parameters

vue
<script setup>
const employeeId = ref(123)

// Use a function for reactive paths
const { data } = useApi(
  () => `/api/organizations/employees/${employeeId.value}`,
  { lazy: true }
)
</script>

Manual Refresh

vue
<script setup>
const { data, refresh } = useOrgApi('/api/organizations/employees', {
  lazy: true
})

const handleRefresh = () => refresh()
</script>

Dependent Requests

vue
<script setup>
const { data: user } = useApi('/api/v1/auth/me', { lazy: true })

const { data: settings } = useApi(
  () => `/api/users/${user.value?.id}/settings`,
  {
    lazy: true,
    immediate: computed(() => !!user.value?.id)
  }
)
</script>

Key Options

typescript
useApi('/api/endpoint', {
  lazy: true,              // ← Use this! Don't block page load
  query: { ... },          // Query params
  immediate: false,        // Don't execute until execute() called
  watch: [someRef],        // Refetch when these change
})

Only for GET Requests

useApi/useOrgApi are designed for fetching data (GET requests). For POST/PUT/DELETE operations, use $apiFetch instead.

Best Practices

Do's ✅

  • Always use lazy: true for better UX
  • Use useOrgApi for organization data
  • Use computed for reactive queries
  • Handle loading and error states

Don'ts ❌

  • Don't use for form submissions → use $apiFetch
  • Don't use useFetch directly → use our wrappers
  • Don't forget lazy: true

Error Handling

Errors are automatically typed, no type guard needed:

vue
<script setup>
const { data, error } = useOrgApi('/api/organizations/employees/123')

// error.value is fully typed
if (error.value?.statusCode === 404) {
  // Handle not found
}
</script>

404 Navigation Pattern

Important: On detail routes (/resource/id/*), always navigate back on 404:

vue
<script setup>
const route = useRoute('hr-orgId-employees-module-id')
const employeeId = computed(() => route.params.id)

const { data, error } = useOrgApi(`/api/organizations/employees/${employeeId.value}`)

// Navigate back to list on 404 (org switch, deleted resource, etc.)
watch(error, (err) => {
  if (err?.statusCode === 404) {
    navigateTo({ name: 'hr-orgId-employees-module', params: { module: route.params.module } })
  }
})
</script>

Why? 404s on detail routes typically happen when users switch orgs or return to old bookmarks. Navigating back provides better UX than an error page.

See Error Handling → for complete patterns.


Next: Learn $apiFetch → | Back to Overview → | Error Handling →