Overview
TheApiClient class provides a fetch-based HTTP client for communicating with external backend services. It automatically manages JWT bearer tokens issued by better-auth, including intelligent caching with a 10-second expiration buffer.
Key Features:
- Zero external dependencies (uses native fetch)
- Automatic JWT token injection and caching
- 10-second expiration buffer to prevent expired token usage
- Standardized error handling
- Type-safe response wrapper
How It Works
- The client obtains a JWT from better-auth via
authClient.token() - The JWT is cached in memory and reused until it’s close to expiry (10s buffer)
- Each request includes the JWT in the
Authorization: Bearer <token>header - Your backend service verifies this token using the JWKS endpoint exposed by better-auth at
/api/auth/jwks
Configuration
Set the backend API URL using an environment variable:.env.local
http://localhost:8080 for local development.
JWT Token Caching
The client implements intelligent token caching to minimize requests to the auth server:- Tokens are cached in memory after the first request
- Before each request, the client checks if the cached token is still valid
- A 10-second buffer is added to the expiration check to prevent using tokens that are about to expire
- If the token is expired or within 10 seconds of expiration, a fresh token is automatically fetched
Usage
Basic Import
Making Authenticated Requests
The client exposes a privaterequest() method that’s used internally. For your own endpoints, you can extend the ApiClient class or use the exported instance:
Extending the Client
Add your own API methods by extending the class:ApiClient Class
Constructor
The base URL for your backend API service. Defaults to the value of
NEXT_PUBLIC_BACKEND_API_URL or http://localhost:8080.Methods
request()
Makes an authenticated HTTP request to the backend. Automatically injects the JWT bearer token into the Authorization header.The API path (e.g.,
/api/users/me). This is appended to the base URL.Standard fetch
RequestInit options including method, body, headers, etc. Headers are merged with default headers (Content-Type and Authorization).The parsed JSON response data. Only present if the request was successful.
An error message if the request failed. Contains either the API error message or a client-side error description.
The HTTP status code. Returns
0 if the request failed before reaching the server.verifyAuth()
Example method that verifies the current user’s authentication against the backend.Whether the authentication token is valid.
The authenticated user’s ID, if valid.
The authenticated user’s email, if valid.
getToken()
Retrieves a valid JWT token, using the cache when possible.- Checks if the cached token is still valid (using
isTokenValid()) - Returns the cached token if valid
- Requests a new JWT from better-auth if the cache is expired or empty
- Updates the cache with the new token
- Returns the token or
nullif authentication failed
Types
ApiResponse<T>
Standard API response wrapper with typed data or error.The response data. Type parameter
T allows for type-safe responses.Error message if the request failed. Mutually exclusive with
data.HTTP status code. Returns
0 for network errors or exceptions.UserProfile
Example user profile shape. Adjust to match your backend schema.Unique user identifier.
User’s email address.
User’s display name.
AuthVerifyResponse
Example auth verification response. Adjust to match your backend schema.Whether the JWT token is valid.
The authenticated user’s ID if the token is valid.
The authenticated user’s email if the token is valid.
Complete Example
Here’s a complete example showing how to create a custom API client with multiple endpoints:Security Notes
- Tokens are short-lived JWTs signed with the server’s private key (Ed25519 by default)
- The backend should always validate the token signature via JWKS, not just decode it
- In production, always use HTTPS for both the auth server and the backend
- The token cache is stored in memory only and is cleared when the page reloads
- Never log or expose JWT tokens in client-side code
Comparison with Axios Client
| Feature | Fetch Client | Axios Client |
|---|---|---|
| Dependencies | Zero (native fetch) | Requires axios package |
| Token Caching | Manual with 10s buffer | Delegated to better-auth |
| Interceptors | Not available | Request/response interceptors |
| Bundle Size | Smaller | Larger |
| Browser Support | Modern browsers | Wider compatibility |
| Cancel Tokens | AbortController | Built-in |
| Progress Events | Limited | Full support |
- Minimal bundle size
- Modern browser-only apps
- Simple request/response patterns
- Advanced interceptor patterns
- Upload/download progress tracking
- Request cancellation
- Older browser support