# Frameworks (/docs/frameworks)



The client works with any framework. It sits *under* your data library and replaces your fetch or axios layer, not TanStack Query or SWR.

## TanStack Query [#tanstack-query]

Failures throw, which is what Query expects. Pass `signal` so Query can cancel requests:

```ts
export function useUsers(page = 1) {
  return useQuery({
    queryKey: ["users", page],
    queryFn: ({ signal }) =>
      api.get<User[]>("/users", { params: { page }, signal }).then((r) => r.data),
  });
}

export function useCreateUser() {
  const qc = useQueryClient();
  return useMutation({
    mutationFn: (input: NewUser) => api.post<User>("/users", input),
    onSuccess: () => qc.invalidateQueries({ queryKey: ["users"] }),
  });
}
```

## SWR [#swr]

```ts
const fetcher = (url: string) => api.get(url).then((r) => r.data);
const { data, error } = useSWR<User[]>("/users", fetcher);
```

## Next.js (App Router) [#nextjs-app-router]

In the browser, create one client in a client-only module:

```ts title="lib/api.ts"
"use client";
import { createClient } from "@mrzr/api-client";

export const api = createClient({ baseUrl: process.env.NEXT_PUBLIC_API_URL });
```

On the server, create a client **per request** and destroy it afterwards. A module-level client would share one user's token with everyone:

```ts title="lib/server-api.ts"
import { createClient, type ApiClient } from "@mrzr/api-client";
import { cookies } from "next/headers";

export async function withApi<T>(fn: (api: ApiClient) => Promise<T>): Promise<T> {
  const api = createClient({ baseUrl: process.env.API_URL, multiTab: false });
  const token = (await cookies()).get("access_token")?.value;
  if (token) await api.setTokens({ accessToken: token });
  try {
    return await fn(api);
  } finally {
    api.destroy();
  }
}
```

The Web Worker is skipped on the server automatically. Next's `cache` options pass straight through to `fetch`.

## Vue [#vue]

```ts
import { ref, onUnmounted } from "vue";
import { api } from "@/lib/api";

export function useAuth() {
  const isAuthenticated = ref(false);
  api.getAuthState().then((s) => (isAuthenticated.value = s.isAuthenticated));
  const off = api.onAuthStateChange((s) => (isAuthenticated.value = s.isAuthenticated));
  onUnmounted(off);
  return { isAuthenticated };
}
```

## Setting `baseUrl` with environment variables [#setting-baseurl-with-environment-variables]

If you leave out `baseUrl`, the client reads the first of these that is set: `NEXT_PUBLIC_API_URL`, `NEXT_PUBLIC_API_BASE_URL`, `NEXT_PUBLIC_BASE_URL`, `VITE_API_URL`, `VITE_API_BASE_URL`, `VITE_BASE_URL`, `NUXT_PUBLIC_API_URL`, `REACT_APP_API_URL`, `EXPO_PUBLIC_API_URL`, `PUBLIC_API_URL`, `PUBLIC_API_BASE_URL`, `API_URL`, `API_BASE_URL`. In a browser with none set, it uses the page's origin.

Passing it explicitly is clearer: `createClient({ baseUrl: process.env.NEXT_PUBLIC_API_URL })`.
