useFetch Infers Your Server Route Types
Typed API Routes
When you call useFetch with an internal /api/... path, Nuxt infers the response type from the server handler's return type — no manual generics needed.
What you'll learn
- Annotate handler return types so Nuxt can infer them
- See useFetch pick up the inferred types on the client
- Cast manually with a generic parameter when inference falls short
One of Nuxt’s secret superpowers: when your useFetch URL points at an internal route, the result
type is inferred from the handler’s return annotation. End-to-end typing without any code
generation.
Annotate the Handler
// server/api/users.get.ts
export interface User {
id: string
name: string
email: string
}
export default defineEventHandler(async (): Promise<User[]> => {
return [
{ id: '1', name: 'Ada', email: 'ada@example.com' },
{ id: '2', name: 'Grace', email: 'grace@example.com' },
]
}) Infer on the Client
<script setup lang="ts">
const { data, pending, error } = await useFetch('/api/users')
// data is Ref<User[] | null>, fully typed — no generic needed
</script>
<template>
<ul v-if="data">
<li v-for="u in data" :key="u.id">{{ u.name }} - {{ u.email }}</li>
</ul>
</template> Hover over data in your editor — it knows the response shape.
Dynamic Routes
The inferred type works for routes with bracket params too:
// server/api/users/[id].get.ts
export default defineEventHandler(async (event): Promise<User> => {
const id = getRouterParam(event, 'id')!
return { id, name: 'Ada', email: 'ada@example.com' }
}) const route = useRoute()
const { data } = await useFetch(() => `/api/users/${route.params.id}`)
// data: Ref<User | null> When to Use a Generic
For external APIs, or when inference can’t follow you (conditional logic, generic helpers), pass a
generic to useFetch:
interface PostsResponse {
posts: { id: number; title: string }[]
}
const { data } = await useFetch<PostsResponse>('https://api.example.com/posts') Tips
- Always annotate the handler’s return type explicitly. Inferring from the function body works but is fragile — small refactors silently change the public type.
- Export your handler interfaces from a shared
types/folder so client code can reference them for things like form input shapes.