Skip to content

Commit 0ff89d2

Browse files
author
Scott Kurtzeborn
committed
Refactor: Centralize API config and error handling
1 parent f68b7ea commit 0ff89d2

File tree

6 files changed

+49
-34
lines changed

6 files changed

+49
-34
lines changed

web/src/api.js

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
/**
2+
* Centralized API configuration
3+
*/
4+
5+
// API base URL - use environment variable in production, empty string for local dev with proxy
6+
export const API_BASE = import.meta.env.VITE_API_URL || '';
7+
8+
/**
9+
* Parse API response, handling both success and error cases
10+
* @param {Response} response - Fetch response
11+
* @returns {Promise<{data: any, ok: boolean}>}
12+
*/
13+
export async function parseApiResponse(response) {
14+
const text = await response.text();
15+
const data = text ? JSON.parse(text) : {};
16+
return { data, ok: response.ok };
17+
}
18+
19+
/**
20+
* Extract error message from API error response
21+
* @param {any} data - Parsed response data
22+
* @param {string} fallback - Fallback error message
23+
* @returns {string}
24+
*/
25+
export function getErrorMessage(data, fallback = 'An error occurred') {
26+
if (typeof data.error === 'string') {
27+
return data.error;
28+
}
29+
return data.error?.message || fallback;
30+
}

web/src/components/CreateUrl.jsx

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
import React, { useState } from 'react';
22
import { useAuth } from '../context/AuthContext';
3-
4-
// API base URL - use environment variable in production
5-
const API_BASE = import.meta.env.VITE_API_URL || '';
3+
import { API_BASE, parseApiResponse, getErrorMessage } from '../api';
64

75
function CreateUrl({ onSuccess }) {
86
const { getAccessToken } = useAuth();
@@ -28,12 +26,11 @@ function CreateUrl({ onSuccess }) {
2826
body: JSON.stringify({ url }),
2927
});
3028

31-
if (!response.ok) {
32-
const data = await response.json();
33-
throw new Error(data.error || 'Failed to create URL');
34-
}
29+
const { data, ok } = await parseApiResponse(response);
3530

36-
const data = await response.json();
31+
if (!ok) {
32+
throw new Error(getErrorMessage(data, 'Failed to create URL'));
33+
}
3734
setSuccess(`Created: ${data.shortUrl}`);
3835
setUrl('');
3936

web/src/components/Dashboard.jsx

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
1-
import React, { useState, useEffect } from 'react';
1+
import React, { useState, useEffect, useCallback } from 'react';
22
import { useAuth } from '../context/AuthContext';
3-
4-
// API base URL - use environment variable in production
5-
const API_BASE = import.meta.env.VITE_API_URL || '';
3+
import { API_BASE } from '../api';
64

75
function Dashboard() {
86
const { getAccessToken } = useAuth();
@@ -14,11 +12,7 @@ function Dashboard() {
1412
const [sortBy, setSortBy] = useState('date');
1513
const [sortOrder, setSortOrder] = useState('desc');
1614

17-
useEffect(() => {
18-
fetchUrls();
19-
}, [page, sortBy, sortOrder]);
20-
21-
const fetchUrls = async () => {
15+
const fetchUrls = useCallback(async () => {
2216
setLoading(true);
2317
setError(null);
2418

@@ -45,7 +39,11 @@ function Dashboard() {
4539
} finally {
4640
setLoading(false);
4741
}
48-
};
42+
}, [getAccessToken, page, sortBy, sortOrder]);
43+
44+
useEffect(() => {
45+
fetchUrls();
46+
}, [fetchUrls]);
4947

5048
const handleDelete = async (id) => {
5149
if (!confirm('Are you sure you want to delete this URL?')) {

web/src/components/Settings.jsx

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
import React, { useState } from 'react';
22
import { useAuth } from '../context/AuthContext';
3-
4-
// API base URL - use environment variable in production
5-
const API_BASE = import.meta.env.VITE_API_URL || '';
3+
import { API_BASE, parseApiResponse, getErrorMessage } from '../api';
64

75
function Settings() {
86
const { getAccessToken } = useAuth();
@@ -28,14 +26,10 @@ function Settings() {
2826
body: JSON.stringify({ email }),
2927
});
3028

31-
const text = await response.text();
32-
const data = text ? JSON.parse(text) : {};
29+
const { data, ok } = await parseApiResponse(response);
3330

34-
if (!response.ok) {
35-
const errorMessage = typeof data.error === 'string'
36-
? data.error
37-
: data.error?.message || 'Failed to add user';
38-
throw new Error(errorMessage);
31+
if (!ok) {
32+
throw new Error(getErrorMessage(data, 'Failed to add user'));
3933
}
4034

4135
setSuccess(`User added! Remaining invites today: ${data.remainingInvites}`);

web/src/context/AuthContext.jsx

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,10 @@
11
import React, { createContext, useContext, useState, useEffect, useCallback, useMemo } from 'react';
22
import { useMsal, useIsAuthenticated } from '@azure/msal-react';
33
import { loginRequest } from '../authConfig';
4+
import { API_BASE } from '../api';
45

56
const AuthContext = createContext(null);
67

7-
// API base URL - use environment variable in production, empty string for local dev with proxy
8-
const API_BASE_URL = import.meta.env.VITE_API_URL || '';
9-
108
export function AuthProvider({ children }) {
119
const { instance, accounts } = useMsal();
1210
const isAuthenticated = useIsAuthenticated();
@@ -19,7 +17,7 @@ export function AuthProvider({ children }) {
1917

2018
const checkUserAllowed = useCallback(async (email) => {
2119
try {
22-
const response = await fetch(`${API_BASE_URL}/api/auth/check?email=${encodeURIComponent(email)}`);
20+
const response = await fetch(`${API_BASE}/api/auth/check?email=${encodeURIComponent(email)}`);
2321
if (response.ok) {
2422
const data = await response.json();
2523
setIsAllowed(data.allowed);

workers/src/index.js

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -119,5 +119,3 @@ async function trackClick(id, env) {
119119
console.error('Error tracking click:', error);
120120
}
121121
}
122-
123-

0 commit comments

Comments
 (0)