Nuxt is a free and open-source framework with an intuitive and extendable way to create type-safe, performant and production-grade full-stack web applications and websites with Vue.js.
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Comprehensive data fetching system with server-side rendering support, caching, loading states, and error handling. Nuxt provides powerful composables for fetching data with automatic SSR hydration, request deduplication, and reactive updates.
Note: Examples use
$fetch, which is Nuxt's built-in fetch utility available globally in Nuxt applications. For external usage, import it from 'ofetch'.
Handle asynchronous data fetching with caching, loading states, and error handling.
/**
* Handle asynchronous data fetching with caching and loading states
* @param key - Unique key for caching the data
* @param handler - Function that returns a Promise resolving to the data
* @param options - Configuration options for the async data
* @returns AsyncData object with data, pending, error, and refresh properties
*/
function useAsyncData<DataT, ErrorT = Error>(
key: string,
handler: () => Promise<DataT>,
options?: AsyncDataOptions<DataT>
): AsyncData<DataT, ErrorT>;
/**
* Lazy version of useAsyncData that doesn't block navigation
* @param key - Unique key for caching the data
* @param handler - Function that returns a Promise resolving to the data
* @param options - Configuration options for the async data
* @returns AsyncData object with data, pending, error, and refresh properties
*/
function useLazyAsyncData<DataT, ErrorT = Error>(
key: string,
handler: () => Promise<DataT>,
options?: AsyncDataOptions<DataT>
): AsyncData<DataT, ErrorT>;
interface AsyncData<DataT, ErrorT> {
/** The fetched data */
data: Ref<DataT | null>;
/** Whether the data is being fetched */
pending: Ref<boolean>;
/** Any error that occurred during fetching */
error: Ref<ErrorT | null>;
/** Function to refresh the data */
refresh: () => Promise<void>;
/** Function to execute the handler */
execute: () => Promise<void>;
/** Current request status */
status: Ref<AsyncDataRequestStatus>;
}
interface AsyncDataOptions<ResT, DataT = ResT, PickKeys extends KeysOf<DataT> = KeysOf<DataT>, DefaultT = null> {
/** Whether to fetch on server-side */
server?: boolean;
/** Whether to fetch on client-side */
client?: boolean;
/** Whether to use lazy loading (non-blocking) */
lazy?: boolean;
/** Whether to fetch immediately */
immediate?: boolean;
/** Default value factory */
default?: () => DefaultT | Ref<DefaultT>;
/** Transform function for the data */
transform?: (input: ResT) => DataT;
/** Keys to pick from the result */
pick?: PickKeys[];
/** Reactive sources to watch for changes */
watch?: MultiWatchSources;
/** Whether to perform deep watching */
deep?: boolean;
/** Whether to dedupe identical requests */
dedupe?: "cancel" | "defer";
}
type AsyncDataRequestStatus = "idle" | "pending" | "success" | "error";Usage Examples:
// Basic async data
const { data: users, pending, error } = await useAsyncData("users", () =>
$fetch("/api/users")
);
// With options
const { data: posts, refresh } = await useAsyncData(
"posts",
() => $fetch("/api/posts"),
{
server: true,
client: false,
default: () => [],
transform: (data: any[]) => data.slice(0, 10)
}
);
// Lazy loading (non-blocking)
const { data: comments } = await useLazyAsyncData(
"comments",
() => $fetch(`/api/posts/${route.params.id}/comments`)
);
// With reactive dependencies
const route = useRoute();
const { data: product } = await useAsyncData(
`product-${route.params.id}`,
() => $fetch(`/api/products/${route.params.id}`),
{
watch: [() => route.params.id]
}
);
// Transform and pick data
const { data: userProfile } = await useAsyncData(
"profile",
() => $fetch("/api/user/profile"),
{
pick: ["id", "name", "email"],
transform: (user: any) => ({
...user,
displayName: `${user.firstName} ${user.lastName}`
})
}
);Fetch data from API endpoints with SSR support and request optimization.
/**
* Fetch data from an API endpoint with SSR support
* @param request - URL string, Request object, or reactive reference
* @param opts - Fetch and async data options
* @returns AsyncData with fetched data
*/
function useFetch<ResT = any, ErrorT = FetchError>(
request: string | Request | Ref<string | Request> | (() => string | Request),
opts?: UseFetchOptions<ResT>
): AsyncData<ResT, ErrorT>;
/**
* Lazy version of useFetch that doesn't block navigation
* @param request - URL string, Request object, or reactive reference
* @param opts - Fetch and async data options
* @returns AsyncData with fetched data
*/
function useLazyFetch<ResT = any, ErrorT = FetchError>(
request: string | Request | Ref<string | Request> | (() => string | Request),
opts?: UseFetchOptions<ResT>
): AsyncData<ResT, ErrorT>;
interface UseFetchOptions<ResT = any, DataT = ResT, PickKeys extends KeysOf<DataT> = KeysOf<DataT>, DefaultT = null, R extends NitroFetchRequest = string, M extends AvailableRouterMethod<R> = AvailableRouterMethod<R>> extends AsyncDataOptions<ResT, DataT, PickKeys, DefaultT>, FetchOptions<R, M> {
/** Request method */
method?: M;
/** Request body */
body?: RequestInit["body"] | Record<string, any>;
/** Request headers */
headers?: Record<string, string> | [key: string, value: string][] | Headers;
/** Query parameters */
query?: SearchParams;
/** Request parameters */
params?: SearchParams;
/** Base URL for the request */
baseURL?: string;
/** Request timeout in milliseconds */
timeout?: number;
/** Request retry configuration */
retry?: number | false;
/** Whether to retry on status codes */
retryStatusCodes?: number[];
/** Delay between retries */
retryDelay?: number;
/** Request interceptors */
onRequest?: (context: FetchContext) => Promise<void> | void;
/** Request error handlers */
onRequestError?: (context: FetchContext & { error: FetchError }) => Promise<void> | void;
/** Response interceptors */
onResponse?: (context: FetchContext & { response: FetchResponse<ResT> }) => Promise<void> | void;
/** Response error handlers */
onResponseError?: (context: FetchContext & { response: FetchResponse<ResT> }) => Promise<void> | void;
}Usage Examples:
// Basic fetch
const { data: users } = await useFetch("/api/users");
// With method and body
const { data: newUser } = await useFetch("/api/users", {
method: "POST",
body: { name: "John", email: "john@example.com" }
});
// With query parameters
const { data: products } = await useFetch("/api/products", {
query: { category: "electronics", limit: 20 }
});
// Reactive URL
const route = useRoute();
const { data: user } = await useFetch(() => `/api/users/${route.params.id}`);
// With headers and interceptors
const { data, error } = await useFetch("/api/protected", {
headers: {
Authorization: `Bearer ${token}`
},
onRequest({ request, options }) {
console.log("Making request to:", request);
},
onResponse({ response }) {
console.log("Response status:", response.status);
},
onResponseError({ response }) {
console.error("Request failed:", response.status);
}
});
// Lazy fetch with transform
const { data: processedData } = await useLazyFetch("/api/raw-data", {
transform: (data: any[]) => data.map(item => ({
id: item.id,
title: item.title.toUpperCase(),
createdAt: new Date(item.created_at)
}))
});Manage and refresh cached async data.
/**
* Get existing async data by key
* @param key - The key used when calling useAsyncData
* @returns Object with data reference
*/
function useNuxtData<DataT = any>(key: string): {
data: Ref<DataT | null>;
};
/**
* Refresh cached async data by key(s)
* @param keys - Optional key or keys to refresh, refreshes all if not provided
* @returns Promise resolving when refresh is complete
*/
function refreshNuxtData(keys?: string | string[]): Promise<void>;
/**
* Clear cached async data
* @param keys - Keys to clear or filter function
*/
function clearNuxtData(keys?: string | string[] | ((key: string) => boolean)): void;Usage Examples:
// Access existing data
const { data: users } = useNuxtData("users");
// Refresh specific data
await refreshNuxtData("users");
// Refresh multiple keys
await refreshNuxtData(["users", "posts"]);
// Refresh all data
await refreshNuxtData();
// Clear specific data
clearNuxtData("users");
// Clear multiple keys
clearNuxtData(["users", "posts"]);
// Clear with filter
clearNuxtData((key) => key.startsWith("user-"));
// Manual refresh with useAsyncData
const { data: posts, refresh } = await useAsyncData("posts", () =>
$fetch("/api/posts")
);
// Refresh this specific data
await refresh();// Dependent data fetching
const { data: user } = await useAsyncData("user", () =>
$fetch("/api/user")
);
const { data: userPosts } = await useAsyncData(
"user-posts",
() => $fetch(`/api/users/${user.value?.id}/posts`),
{
// Only fetch when user is available
server: false,
watch: [user],
immediate: false
}
);
// Conditional fetching
const route = useRoute();
const shouldFetch = computed(() => route.name === "dashboard");
const { data: analytics } = await useAsyncData(
"analytics",
() => $fetch("/api/analytics"),
{
server: shouldFetch.value,
client: shouldFetch.value
}
);
// Polling data
const { data: liveData, refresh } = await useAsyncData(
"live-data",
() => $fetch("/api/live-data")
);
// Set up polling
const { pause, resume } = useIntervalFn(async () => {
await refresh();
}, 5000);
// Optimistic updates
const { data: todos, refresh } = await useAsyncData("todos", () =>
$fetch("/api/todos")
);
async function addTodo(newTodo: Todo) {
// Optimistically update
todos.value = [...(todos.value || []), { ...newTodo, id: Date.now() }];
try {
await $fetch("/api/todos", {
method: "POST",
body: newTodo
});
// Refresh to get server state
await refresh();
} catch (error) {
// Revert on error
await refresh();
throw error;
}
}// Basic error handling
const { data, error, pending } = await useAsyncData("users", async () => {
try {
return await $fetch("/api/users");
} catch (err) {
throw createError({
statusCode: 500,
statusMessage: "Failed to fetch users"
});
}
});
// Global error handling
const { data: posts } = await useFetch("/api/posts", {
onResponseError({ response }) {
if (response.status === 401) {
throw createError({
statusCode: 401,
statusMessage: "Unauthorized"
});
}
}
});
// Retry logic
const { data: unreliableData } = await useFetch("/api/unreliable", {
retry: 3,
retryDelay: 1000,
retryStatusCodes: [408, 409, 425, 429, 500, 502, 503, 504]
});interface FetchError extends Error {
request?: RequestInfo;
options?: RequestInit;
response?: Response;
data?: any;
status?: number;
statusText?: string;
}
interface FetchContext<T = any, R extends NitroFetchRequest = NitroFetchRequest> {
request: R;
options: FetchOptions<R>;
response?: FetchResponse<T>;
error?: FetchError;
}
interface FetchResponse<T> extends Response {
_data?: T;
}
type SearchParams = Record<string, any> | [key: string, value: string][] | string | URLSearchParams;
type MultiWatchSources = (WatchSource<unknown> | object)[];
type KeysOf<T> = Array<
T extends T
? keyof T extends string
? keyof T
: string
: never
>;
type AvailableRouterMethod<R extends NitroFetchRequest> =
R extends string
? "GET" | "HEAD" | "PATCH" | "POST" | "PUT" | "DELETE" | "OPTIONS"
: "GET" | "HEAD" | "PATCH" | "POST" | "PUT" | "DELETE" | "OPTIONS";
type NitroFetchRequest = string | Request;Install with Tessl CLI
npx tessl i tessl/npm-nuxt