Handle fetch fail in client app, fix data binding in location data (#243)

* Improve error handling for server rendered routes

* Fix data binding issue for country/state codes (use alias in sql)
This commit is contained in:
2026-08-23 03:26:48 -04:00
committed by GitHub
parent 1621313daa
commit 5816634809
10 changed files with 97 additions and 185 deletions

View File

@@ -1,3 +1,5 @@
import { data } from 'react-router';
const API_BASE_URL = process.env.API_BASE_URL || 'http://localhost:8080';
interface ApiResponse<T> {
@@ -24,32 +26,49 @@ export interface Brewery {
location: BreweryLocation | null;
}
const fetchApi = async (path: string): Promise<Response> => {
try {
return await fetch(`${API_BASE_URL}${path}`);
} catch {
throw data('The brewery service is unreachable right now. Please try again in a moment.', {
status: 503,
statusText: 'Service Unavailable',
});
}
};
export const getBreweries = async (limit?: number, offset?: number): Promise<Brewery[]> => {
const params = new URLSearchParams();
if (limit !== undefined) params.set('limit', String(limit));
if (offset !== undefined) params.set('offset', String(offset));
const res = await fetch(`${API_BASE_URL}/api/brewery?${params}`);
const res = await fetchApi(`/api/brewery?${params}`);
if (!res.ok) {
throw new Error(`Failed to load breweries (${res.status})`);
throw data(`Failed to load breweries (${res.status}).`, {
status: res.status,
statusText: res.statusText,
});
}
const data: ApiResponse<Brewery[]> = await res.json();
return data.payload;
const body: ApiResponse<Brewery[]> = await res.json();
return body.payload;
};
export const getBreweryById = async (id: string): Promise<Brewery | null> => {
const res = await fetch(`${API_BASE_URL}/api/brewery/${encodeURIComponent(id)}`);
const res = await fetchApi(`/api/brewery/${encodeURIComponent(id)}`);
if (res.status === 404) {
return null;
}
if (!res.ok) {
throw new Error(`Failed to load brewery (${res.status})`);
throw data(`Failed to load brewery (${res.status}).`, {
status: res.status,
statusText: res.statusText,
});
}
const data: ApiResponse<Brewery> = await res.json();
return data.payload;
const body: ApiResponse<Brewery> = await res.json();
return body.payload;
};

View File

@@ -1,3 +1,4 @@
import RouteErrorState from '../../../components/ui/error/RouteErrorState';
import BreweryCard from '../components/BreweryCard';
import { getBreweries } from '../breweries.server';
import type { Route } from './+types/breweries';
@@ -37,3 +38,7 @@ const Breweries = ({ loaderData }: Route.ComponentProps) => {
};
export default Breweries;
export const ErrorBoundary = ({ error }: Route.ErrorBoundaryProps) => (
<RouteErrorState error={error} />
);

View File

@@ -1,11 +1,12 @@
import { data, Link } from 'react-router';
import RouteErrorState from '../../../components/ui/error/RouteErrorState';
import { getBreweryById } from '../breweries.server';
import type { Route } from './+types/brewery-detail';
export const loader = async ({ params }: Route.LoaderArgs) => {
const brewery = await getBreweryById(params.id);
if (!brewery) {
throw data('Brewery not found.', { status: 404 });
throw data('Brewery not found.', { status: 404, statusText: 'Not Found' });
}
return { brewery };
@@ -57,3 +58,7 @@ const BreweryDetail = ({ loaderData }: Route.ComponentProps) => {
};
export default BreweryDetail;
export const ErrorBoundary = ({ error }: Route.ErrorBoundaryProps) => (
<RouteErrorState error={error} />
);