11// Helpers for working with Trading Strategy public APIs
22import { type NumericRange , error } from '@sveltejs/kit' ;
3+ import type { StandardSchemaV1 } from '@standard-schema/spec' ;
34import { backendUrl } from '$lib/config' ;
45
56type Params = Record < string , string > ;
@@ -12,10 +13,22 @@ const controllers: Record<string, AbortController> = {};
1213 * @param fetch SvelteKit's fetch function
1314 * @param endpoint final path segment of endpoint, e.g.: 'chains', 'pairs'
1415 * @param params URL paramaters as a record of string values
15- * @param abortPrevious set to true to abort pending requets to the same endpoint
16- * @returns the deserialised JSON payload
16+ * @param options optional configuration
17+ * @param options.abortPrevious abort any pending requests to the same endpoint
18+ * @param options.schema Standard Schema for response validation (e.g., `z.object({ ... })`)
19+ * @returns the deserialized and optionally validated JSON payload
1720 */
18- export async function fetchPublicApi ( fetch : Fetch , endpoint : string , params : Params = { } , abortPrevious = false ) {
21+ export async function fetchPublicApi < T = unknown > (
22+ fetch : Fetch ,
23+ endpoint : string ,
24+ params : Params = { } ,
25+ options ?: {
26+ abortPrevious ?: boolean ;
27+ schema ?: StandardSchemaV1 < unknown , T > ;
28+ }
29+ ) : Promise < T > {
30+ const { abortPrevious = false , schema } = options ?? { } ;
31+
1932 let signal : AbortSignal | undefined = undefined ;
2033
2134 if ( abortPrevious ) {
@@ -26,16 +39,28 @@ export async function fetchPublicApi(fetch: Fetch, endpoint: string, params: Par
2639
2740 const searchParams = new URLSearchParams ( params ) ;
2841
42+ let data : unknown ;
43+
2944 try {
3045 const resp = await fetch ( `${ backendUrl } /${ endpoint } ?${ searchParams } ` , { signal } ) ;
3146 if ( ! resp . ok ) throw await publicApiError ( resp ) ;
32- return resp . json ( ) ;
47+ data = await resp . json ( ) ;
3348 } catch ( e ) {
34- if ( e . name === 'AbortError' ) return ;
49+ if ( ( e as Error ) . name === 'AbortError' ) return undefined as T ;
3550 throw e ;
3651 } finally {
3752 if ( ! signal ?. aborted ) delete controllers [ endpoint ] ;
3853 }
54+
55+ // return raw response payload if no validation schema was provided
56+ if ( ! schema ) return data as T ;
57+
58+ // validate response and return validated/typed payload if successful
59+ const result = await schema [ '~standard' ] . validate ( data ) ;
60+ if ( result . issues ) {
61+ error ( 500 , { message : 'API response validation failed' , ...result } ) ;
62+ }
63+ return result . value ;
3964}
4065
4166/**
@@ -82,5 +107,6 @@ export function optionalDataError(dataType?: string) {
82107 return ( err : Error ) => {
83108 console . error ( `${ errorIntro } ; rendering page without data.` ) ;
84109 console . error ( err ) ;
110+ return undefined ;
85111 } ;
86112}
0 commit comments