feat: add PBHttpError and try/catch in getFirstRecord #6

Merged
ilia merged 1 commits from fix/get-first-record into main 2026-05-21 10:08:05 +00:00
2 changed files with 53 additions and 3 deletions
Showing only changes of commit 9deefaf3fc - Show all commits
+16 -3
View File
@@ -1,3 +1,4 @@
import { PBHttpError } from './error';
import type { ListResponse } from './types'; import type { ListResponse } from './types';
/* /*
@@ -66,7 +67,7 @@ export async function getCollection<T>(collection: string, options: PBFetchOptio
}); });
if (!res.ok) { if (!res.ok) {
throw new Error(`PocketBase ${res.status} ${res.statusText} on collection "${collection}"`); throw new PBHttpError(res.status, collection, res.statusText);
} }
return res.json(); return res.json();
@@ -74,8 +75,20 @@ export async function getCollection<T>(collection: string, options: PBFetchOptio
/** /**
* Fetch the first record matching an optional filter from a PocketBase collection. * Fetch the first record matching an optional filter from a PocketBase collection.
*
* Returns null on connection failure (e.g. PocketBase unreachable during build)
* so prerendering doesn't crash. HTTP errors (4xx/5xx) are rethrown — PB is
* reachable but something is genuinely wrong, which shouldn't be silently hidden.
*/ */
export async function getFirstRecord<T>(collection: string, options: PBFetchOptions = {}): Promise<T | null> { export async function getFirstRecord<T>(collection: string, options: PBFetchOptions = {}): Promise<T | null> {
const data = await getCollection<T>(collection, options); try {
return data.items[0] ?? null; const data = await getCollection<T>(collection, options);
return data.items[0] ?? null;
} catch (err) {
if (err instanceof PBHttpError) {
throw err;
}
console.warn(`[getFirstRecord] "${collection}" unreachable — returning null`, err);
return null;
}
} }
+37
View File
@@ -0,0 +1,37 @@
/**
* Error thrown when PocketBase responds with a non-OK HTTP status (4xx/5xx).
*
* Distinguishes *server-responded-with-failure* from *server-unreachable*.
* A connection-level failure (ECONNREFUSED, DNS, the build-time `PB_URL`
* guard) throws a plain `Error`; only an actual HTTP response throws this.
* Callers use `instanceof PBHttpError` to decide whether to swallow the
* failure (connection — safe to ignore at build) or rethrow it (HTTP — a
* real problem that must surface).
*
* @example
* try {
* await getCollection('site_settings');
* } catch (err) {
* if (err instanceof PBHttpError) {
* // PB is up but returned e.g. 403 — log, alert, rethrow
* console.error(`PB returned ${err.status} for ${err.collection}`);
* } else {
* // PB unreachable — acceptable during build, render empty
* }
* }
*/
export class PBHttpError extends Error {
/**
* @param status HTTP status code returned by PocketBase (e.g. 404, 500).
* @param collection Name of the collection that was queried, for context.
* @param statusText HTTP status text from the response (e.g. "Not Found").
*/
constructor(
public readonly status: number,
public readonly collection: string,
statusText: string,
) {
super(`PocketBase ${status} ${statusText} on collection "${collection}"`);
this.name = 'PBHttpError';
}
}