Skip to main content

API Client

NestForge Pro includes a typed fetch wrapper and auth context that handle all communication with the backend.

Fetch wrapper

The API client lives at src/lib/api.ts. It wraps the native fetch API with automatic JSON handling, auth token injection, and response envelope unwrapping.

import { api } from "@/lib/api";

// GET request
const user = await api.get<UserProfile>("/users/me");

// POST request
const team = await api.post<Team>("/teams", { name: "Acme" });

// PATCH request
await api.patch<UserProfile>("/users/me", { name: "Jane" });

// DELETE request
await api.delete("/teams/abc123");

Methods

MethodSignatureHTTP verb
api.getget<T>(path: string): Promise<T>GET
api.postpost<T>(path: string, body?: unknown): Promise<T>POST
api.patchpatch<T>(path: string, body?: unknown): Promise<T>PATCH
api.deletedelete<T>(path: string): Promise<T>DELETE

All methods are generic -- pass a type parameter to get typed responses.

Response envelope

The API returns responses in a standard envelope:

{
"data": { ... },
"message": "Success"
}

The client automatically unwraps the envelope and returns data directly. Errors throw with the message from the response body.

Auth token injection

The client reads accessToken from localStorage and attaches it as a Bearer token on every request:

Authorization: Bearer <token>

No manual token management needed -- the auth context handles storage and refresh.

Base URL

The base URL is resolved from the VITE_API_URL environment variable. In development, this defaults to /api, which Vite proxies to localhost:4000.

const API_URL = import.meta.env.VITE_API_URL || "/api";

Auth context

The auth context at src/lib/auth.tsx manages user state, login, registration, logout, and token refresh.

Setup

The AuthProvider wraps your app and provides auth state to all children:

import { AuthProvider } from "@/lib/auth";

function App() {
return (
<AuthProvider>
<Router />
</AuthProvider>
);
}

useAuth hook

Access auth state and actions anywhere in the component tree:

import { useAuth } from "@/lib/auth";

function Dashboard() {
const { user, isLoading, logout } = useAuth();

if (isLoading) return <Spinner />;
if (!user) return <Navigate to="/login" />;

return (
<div>
<p>Welcome, {user.name}</p>
<button onClick={logout}>Log out</button>
</div>
);
}

Available properties and methods

NameTypeDescription
userUserProfile | nullCurrent authenticated user, or null
isLoadingbooleanTrue while the initial token refresh is in progress
login(email, password) => Promise<void>Log in with email and password
register(name, email, password) => Promise<void>Create a new account
logout() => voidClear tokens and redirect to home
refresh() => Promise<void>Manually refresh the access token

Token storage

Tokens are stored in localStorage:

  • accessToken -- short-lived JWT for API requests
  • refreshToken -- long-lived token for obtaining new access tokens

On mount, the AuthProvider attempts a silent refresh using the stored refresh token. If the refresh fails (expired or missing), the user is logged out.

Vite proxy

During development, the Vite dev server proxies API requests so you don't need CORS configuration. Requests to /api/* on localhost:3000 are forwarded to localhost:4000.

TanStack Query hooks

Reusable query hooks live in src/lib/hooks/. These wrap api.get and api.post calls with TanStack Query for caching, refetching, and optimistic updates:

import { useQuery } from "@tanstack/react-query";
import { api } from "@/lib/api";

export function useTeams() {
return useQuery({
queryKey: ["teams"],
queryFn: () => api.get<Team[]>("/teams"),
});
}

Hooks are organized by domain: teams, admin, settings, billing.