|
| 1 | +--- |
| 2 | +layout: post |
| 3 | +title: "Auto-Inject Bearer Token with a fetch API Auth Helper" |
| 4 | +date: 2025-07-10 09:00:00 +0900 |
| 5 | +categories: [Development, Tips] |
| 6 | +tags: [JavaScript, fetch, Bearer Token, authentication, API] |
| 7 | +author: "Kevin Park" |
| 8 | +lang: en |
| 9 | +excerpt: "Stop repeating Authorization headers — build a fetch wrapper that auto-injects Bearer tokens." |
| 10 | +--- |
| 11 | + |
| 12 | +## Problem |
| 13 | + |
| 14 | +Manually adding `headers: { 'Authorization': 'Bearer ...' }` to every API call. Token refresh and error handling duplicated across each function. |
| 15 | + |
| 16 | +## Solution |
| 17 | + |
| 18 | +```typescript |
| 19 | +let authToken: string | null = null; |
| 20 | + |
| 21 | +export function setAuthToken(token: string | null) { |
| 22 | + authToken = token; |
| 23 | + if (token) localStorage.setItem('auth_token', token); |
| 24 | + else localStorage.removeItem('auth_token'); |
| 25 | +} |
| 26 | + |
| 27 | +function getAuthToken(): string | null { |
| 28 | + if (!authToken) authToken = localStorage.getItem('auth_token'); |
| 29 | + return authToken; |
| 30 | +} |
| 31 | + |
| 32 | +async function fetchWithAuth<T>( |
| 33 | + endpoint: string, |
| 34 | + options: RequestInit = {} |
| 35 | +): Promise<T> { |
| 36 | + const token = getAuthToken(); |
| 37 | + const headers: Record<string, string> = { |
| 38 | + 'Content-Type': 'application/json', |
| 39 | + ...(options.headers as Record<string, string>), |
| 40 | + }; |
| 41 | + if (token) headers['Authorization'] = `Bearer ${token}`; |
| 42 | + |
| 43 | + const res = await fetch(`${API_URL}${endpoint}`, { ...options, headers }); |
| 44 | + |
| 45 | + if (!res.ok) { |
| 46 | + const err = await res.json().catch(() => ({ message: 'Unknown error' })); |
| 47 | + throw new Error(err.message || `HTTP ${res.status}`); |
| 48 | + } |
| 49 | + |
| 50 | + return res.json(); |
| 51 | +} |
| 52 | +``` |
| 53 | + |
| 54 | +## Key Points |
| 55 | + |
| 56 | +- Dual-layer token storage: in-memory variable first, `localStorage` as fallback. Faster than calling `localStorage.getItem` every time. |
| 57 | +- All API functions reduce to `fetchWithAuth<ResponseType>('/endpoint')`. Token management logic lives in one place — easy to add refresh logic later. |
| 58 | +- `res.json().catch()` gracefully handles non-JSON responses (like HTML error pages from the server). |
0 commit comments