Auto-Inject Bearer Token with a fetch API Auth Helper
Problem
Manually adding headers: { 'Authorization': 'Bearer ...' } to every API call. Token refresh and error handling duplicated across each function.
Solution
let authToken: string | null = null;
export function setAuthToken(token: string | null) {
authToken = token;
if (token) localStorage.setItem('auth_token', token);
else localStorage.removeItem('auth_token');
}
function getAuthToken(): string | null {
if (!authToken) authToken = localStorage.getItem('auth_token');
return authToken;
}
async function fetchWithAuth<T>(
endpoint: string,
options: RequestInit = {}
): Promise<T> {
const token = getAuthToken();
const headers: Record<string, string> = {
'Content-Type': 'application/json',
...(options.headers as Record<string, string>),
};
if (token) headers['Authorization'] = `Bearer ${token}`;
const res = await fetch(`${API_URL}${endpoint}`, { ...options, headers });
if (!res.ok) {
const err = await res.json().catch(() => ({ message: 'Unknown error' }));
throw new Error(err.message || `HTTP ${res.status}`);
}
return res.json();
}
Key Points
- Dual-layer token storage: in-memory variable first,
localStorageas fallback. Faster than callinglocalStorage.getItemevery time. - All API functions reduce to
fetchWithAuth<ResponseType>('/endpoint'). Token management logic lives in one place — easy to add refresh logic later. res.json().catch()gracefully handles non-JSON responses (like HTML error pages from the server).