Skip to main content

Overview

ofetch.raw() returns the complete Response object along with the parsed data, giving you access to headers, status codes, and other response metadata.

Basic Usage

const response = await ofetch.raw('/api/users')

console.log(response._data)    // Parsed response body
console.log(response.status)   // 200
console.log(response.headers)  // Headers object
console.log(response.ok)       // true

When to Use

Use ofetch.raw() when you need:
  • Response headers (e.g., pagination info, rate limits)
  • HTTP status codes
  • Response metadata
  • Full Response object for further processing

Response Structure

From src/types.ts:137-139:
export interface FetchResponse<T> extends Response {
  _data?: T;
}
The response object:
  • Extends the standard Response interface
  • Includes all native Response properties and methods
  • Adds _data property with the parsed body

Common Use Cases

Accessing Response Headers

const response = await ofetch.raw('/api/users?page=1')

// Pagination headers
const totalPages = response.headers.get('X-Total-Pages')
const currentPage = response.headers.get('X-Current-Page')

console.log(`Page ${currentPage} of ${totalPages}`)
console.log('Users:', response._data)

Rate Limit Information

const response = await ofetch.raw('https://api.github.com/users/unjs')

const rateLimit = {
  limit: response.headers.get('X-RateLimit-Limit'),
  remaining: response.headers.get('X-RateLimit-Remaining'),
  reset: response.headers.get('X-RateLimit-Reset')
}

console.log(`API calls remaining: ${rateLimit.remaining}/${rateLimit.limit}`)

Checking Status Codes

const response = await ofetch.raw('/api/resource', {
  ignoreResponseError: true
})

if (response.status === 404) {
  console.log('Resource not found')
} else if (response.status === 304) {
  console.log('Not modified, use cached version')
} else if (response.ok) {
  console.log('Success:', response._data)
}

Content Type Detection

const response = await ofetch.raw('/api/download')

const contentType = response.headers.get('Content-Type')
const contentLength = response.headers.get('Content-Length')

console.log(`Downloading ${contentType} (${contentLength} bytes)`)

How It Works

From src/fetch.ts:89-257:
const $fetchRaw: $Fetch["raw"] = async function $fetchRaw<
  T = any,
  R extends ResponseType = "json",
>(_request: FetchRequest, _options: FetchOptions<R> = {}) {
  const context: FetchContext = {
    request: _request,
    options: resolveFetchOptions<R, T>(
      _request,
      _options,
      globalOptions.defaults as unknown as FetchOptions<R, T>,
      Headers
    ),
    response: undefined,
    error: undefined,
  };
  
  // ... request processing ...
  
  context.response = await fetch(
    context.request,
    context.options as RequestInit
  );
  
  // ... response parsing ...
  
  return context.response;
};
The raw method:
  1. Creates a fetch context with request and options
  2. Processes the request (interceptors, URL building, etc.)
  3. Executes the fetch call
  4. Parses the response body into _data
  5. Returns the full Response object

Regular vs Raw Response

From src/fetch.ts:259-262:
const $fetch = async function $fetch(request, options) {
  const r = await $fetchRaw(request, options);
  return r._data;
} as $Fetch;
Regular ofetch():
const data = await ofetch('/api/users')
// Returns only the parsed data
Raw ofetch.raw():
const response = await ofetch.raw('/api/users')
// Returns Response { _data, status, headers, ok, ... }

TypeScript Support

interface User {
  id: number
  name: string
}

const response = await ofetch.raw<User[]>('/api/users')

response._data  // Typed as User[] | undefined
response.status // number
response.headers // Headers

Using with Custom Instances

const apiFetch = ofetch.create({ baseURL: '/api' })

const response = await apiFetch.raw('/users')
console.log(response.headers.get('X-Custom-Header'))

Advanced Example: Cache Headers

const response = await ofetch.raw('/api/data')

const cacheControl = response.headers.get('Cache-Control')
const etag = response.headers.get('ETag')
const lastModified = response.headers.get('Last-Modified')

if (cacheControl?.includes('no-cache')) {
  console.log('Response should not be cached')
}

// Use ETag for conditional requests
if (etag) {
  await ofetch('/api/data', {
    headers: { 'If-None-Match': etag }
  })
}

Build docs developers (and LLMs) love