feat: add error handling and tests for client.ts
This commit is contained in:
@@ -0,0 +1,40 @@
|
|||||||
|
import { afterEach, describe, expect, it, vi } from 'vitest';
|
||||||
|
|
||||||
|
import { PBHttpError } from '../error';
|
||||||
|
import { getCollection } from './client';
|
||||||
|
|
||||||
|
describe('getCollection', () => {
|
||||||
|
afterEach(() => {
|
||||||
|
vi.unstubAllEnvs();
|
||||||
|
vi.unstubAllGlobals();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('when PocketBase is unreachable', () => {
|
||||||
|
it('returns an empty list instead of throwing', async () => {
|
||||||
|
vi.stubEnv('PB_URL', 'http://localhost:8090');
|
||||||
|
vi.stubGlobal('fetch', vi.fn().mockRejectedValue(new TypeError('fetch failed')));
|
||||||
|
|
||||||
|
const result = await getCollection('projects');
|
||||||
|
|
||||||
|
expect(result.items).toEqual([]);
|
||||||
|
expect(result.totalItems).toBe(0);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('when PocketBase returns an HTTP error', () => {
|
||||||
|
it('rethrows PBHttpError', async () => {
|
||||||
|
vi.stubEnv('PB_URL', 'http://localhost:8090');
|
||||||
|
vi.stubGlobal(
|
||||||
|
'fetch',
|
||||||
|
vi.fn().mockResolvedValue({
|
||||||
|
ok: false,
|
||||||
|
status: 403,
|
||||||
|
statusText: 'Forbidden',
|
||||||
|
json: vi.fn(),
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
await expect(getCollection('projects')).rejects.toBeInstanceOf(PBHttpError);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -1,13 +1,10 @@
|
|||||||
import { PBHttpError } from './error';
|
import { PBHttpError } from '../error';
|
||||||
import type { ListResponse } from './types';
|
import type { ListResponse } from '../types';
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Native fetch wrapper for PocketBase API requests.
|
* Native fetch wrapper for PocketBase API requests.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* Required in production; falls back to localhost in development. */
|
|
||||||
const PB_URL = process.env.PB_URL ?? (process.env.NODE_ENV === 'development' ? 'http://127.0.0.1:8090' : undefined);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Options for PocketBase collection fetching.
|
* Options for PocketBase collection fetching.
|
||||||
*/
|
*/
|
||||||
@@ -40,12 +37,15 @@ export type PBFetchOptions = {
|
|||||||
* Fetch a list of records from a PocketBase collection.
|
* Fetch a list of records from a PocketBase collection.
|
||||||
*/
|
*/
|
||||||
export async function getCollection<T>(collection: string, options: PBFetchOptions = {}): Promise<ListResponse<T>> {
|
export async function getCollection<T>(collection: string, options: PBFetchOptions = {}): Promise<ListResponse<T>> {
|
||||||
const { sort, filter, expand, tags, revalidate } = options;
|
/* Required in production; falls back to localhost in development. */
|
||||||
|
const pbUrl = process.env.PB_URL ?? (process.env.NODE_ENV === 'development' ? 'http://127.0.0.1:8090' : undefined);
|
||||||
|
|
||||||
if (!PB_URL) {
|
if (!pbUrl) {
|
||||||
throw new Error('PB_URL is required in production');
|
throw new Error('PB_URL is required in production');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const { sort, filter, expand, tags, revalidate } = options;
|
||||||
|
|
||||||
const params = new URLSearchParams();
|
const params = new URLSearchParams();
|
||||||
if (sort) {
|
if (sort) {
|
||||||
params.set('sort', sort);
|
params.set('sort', sort);
|
||||||
@@ -57,8 +57,9 @@ export async function getCollection<T>(collection: string, options: PBFetchOptio
|
|||||||
params.set('expand', expand);
|
params.set('expand', expand);
|
||||||
}
|
}
|
||||||
|
|
||||||
const url = `${PB_URL}/api/collections/${collection}/records?${params.toString()}`;
|
const url = `${pbUrl}/api/collections/${collection}/records?${params.toString()}`;
|
||||||
|
|
||||||
|
try {
|
||||||
const res = await fetch(url, {
|
const res = await fetch(url, {
|
||||||
next: {
|
next: {
|
||||||
tags: tags ?? [],
|
tags: tags ?? [],
|
||||||
@@ -71,6 +72,13 @@ export async function getCollection<T>(collection: string, options: PBFetchOptio
|
|||||||
}
|
}
|
||||||
|
|
||||||
return res.json();
|
return res.json();
|
||||||
|
} catch (err) {
|
||||||
|
if (err instanceof PBHttpError) {
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
console.warn(`[getCollection] "${collection}" unreachable — returning empty list`, err);
|
||||||
|
return { items: [], page: 1, perPage: 0, totalItems: 0, totalPages: 0 };
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1,2 +1,2 @@
|
|||||||
export * from './client';
|
export * from './client/client';
|
||||||
export * from './types';
|
export * from './types';
|
||||||
|
|||||||
Reference in New Issue
Block a user