Skip to main content
ofetch provides excellent TypeScript support with generic types for type-safe API calls.

Response Type Inference

Use TypeScript generics to specify the expected response type:
import { ofetch } from 'ofetch'

interface User {
  id: number
  name: string
  email: string
}

// Response is typed as User
const user = await ofetch<User>('https://api.example.com/user/123')

console.log(user.name)  // ✅ TypeScript knows this is a string
console.log(user.age)   // ❌ Error: Property 'age' does not exist

Nested Response Types

For APIs that wrap data in a response envelope:
interface Repo {
  id: number
  name: string
  repo: string
  description: string
  stars: number
}

// From examples/type-safety.ts
const { repo } = await ofetch<{ repo: Repo }>(
  'https://ungh.cc/repos/unjs/ofetch'
)

console.log(`The repo ${repo.name} has ${repo.stars} stars.`)
// TypeScript knows repo.name is string and repo.stars is number

Array Responses

interface User {
  id: number
  name: string
}

const users = await ofetch<User[]>('https://api.example.com/users')

users.forEach(user => {
  console.log(user.name)  // ✅ Typed correctly
})

Response Type with responseType Option

The second generic parameter allows you to specify the response format:
// JSON response (default)
const data = await ofetch<User, 'json'>('/api/user')

// Text response
const text = await ofetch<string, 'text'>('/api/data', {
  responseType: 'text'
})

// Blob response
const blob = await ofetch<Blob, 'blob'>('/api/image', {
  responseType: 'blob'
})

// ArrayBuffer response
const buffer = await ofetch<ArrayBuffer, 'arrayBuffer'>('/api/binary', {
  responseType: 'arrayBuffer'
})

// Stream response
const stream = await ofetch<ReadableStream<Uint8Array>, 'stream'>('/api/stream', {
  responseType: 'stream'
})

Type-Safe API Client

Create a fully typed API client:
import { ofetch } from 'ofetch'

interface User {
  id: number
  name: string
  email: string
}

interface CreateUserRequest {
  name: string
  email: string
}

class UserAPI {
  private client = ofetch.create({
    baseURL: 'https://api.example.com'
  })

  async getUser(id: number): Promise<User> {
    return this.client<User>(`/users/${id}`)
  }

  async createUser(data: CreateUserRequest): Promise<User> {
    return this.client<User>('/users', {
      method: 'POST',
      body: data
    })
  }

  async listUsers(): Promise<User[]> {
    return this.client<User[]>('/users')
  }
}

const api = new UserAPI()
const user = await api.getUser(123)
console.log(user.name)  // Fully typed!

Hooks with Type Safety

Hooks are also fully typed:
interface User {
  id: number
  name: string
}

await ofetch<User>('/api/user', {
  onResponse({ response }) {
    // response._data is typed as User
    console.log(response._data.name)
  }
})

Error Handling with Types

import { FetchError } from 'ofetch'

interface ErrorResponse {
  message: string
  code: string
}

try {
  const user = await ofetch<User>('/api/user/123')
} catch (error) {
  if (error instanceof FetchError) {
    const errorData = error.data as ErrorResponse
    console.log(errorData.message)
  }
}

Type Definitions

From src/types.ts:5-16:
export interface $Fetch {
  <T = any, R extends ResponseType = "json">(
    request: FetchRequest,
    options?: FetchOptions<R>
  ): Promise<MappedResponseType<R, T>>;
  
  raw<T = any, R extends ResponseType = "json">(
    request: FetchRequest,
    options?: FetchOptions<R>
  ): Promise<FetchResponse<MappedResponseType<R, T>>>;
  
  native: Fetch;
  create(defaults: FetchOptions, globalOptions?: CreateFetchOptions): $Fetch;
}
The MappedResponseType helper (from src/types.ts:132-135):
export type MappedResponseType<
  R extends ResponseType,
  JsonType = any,
> = R extends keyof ResponseMap ? ResponseMap[R] : JsonType
This means:
  • responseType: 'blob' → returns Blob
  • responseType: 'text' → returns string
  • responseType: 'arrayBuffer' → returns ArrayBuffer
  • responseType: 'stream' → returns ReadableStream<Uint8Array>
  • responseType: 'json' (default) → returns your generic type T

Build docs developers (and LLMs) love