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
| Method | Signature | HTTP verb |
|---|---|---|
api.get | get<T>(path: string): Promise<T> | GET |
api.post | post<T>(path: string, body?: unknown): Promise<T> | POST |
api.patch | patch<T>(path: string, body?: unknown): Promise<T> | PATCH |
api.delete | delete<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
| Name | Type | Description |
|---|---|---|
user | UserProfile | null | Current authenticated user, or null |
isLoading | boolean | True 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 | () => void | Clear 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 requestsrefreshToken-- 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.