Simple API gateway for webhooks
at main 3.4 kB view raw
1import { crypto } from "jsr:@std/crypto" 2import { encodeHex } from "jsr:@std/encoding/hex" 3 4export const kv = await Deno.openKv() 5 6/** 7 * Wraps a promise in a try/catch block and returns a Result object representing 8 * either a successful value or an error. 9 * 10 * This utility function provides a more structured way to handle asynchronous operations 11 * without using try/catch blocks throughout your codebase. It follows a pattern similar 12 * to Rust's Result type, allowing for more predictable error handling. 13 * 14 * @template T - The type of the value returned by the promise on success 15 * @template E - The type of the error object (defaults to Error) 16 * 17 * @param promise - The promise to wrap and execute 18 * 19 * @returns A Promise resolving to a discriminated union object with: 20 * - On success: `{ success: true, value: T, error: null }` 21 * - On failure: `{ success: false, value: null, error: E }` 22 * 23 * @example 24 * // Success case 25 * const successResult = await tryCatch(Promise.resolve('data')); 26 * if (successResult.success) { 27 * console.log(successResult.value); // 'data' 28 * } 29 * 30 * @example 31 * // Error case 32 * const errorResult = await tryCatch(Promise.reject(new Error('Failed'))); 33 * if (!errorResult.success) { 34 * console.error(errorResult.error.message); // 'Failed' 35 * } 36 * 37 * @example 38 * // Using with custom error type 39 * interface ApiError { code: number; message: string } 40 * const apiCall = tryCatch<UserData, ApiError>(fetchUserData()); 41 */ 42export async function tryCatch<T, E = Error>( 43 promise: Promise<T>, 44): Promise< 45 | { 46 success: true 47 value: T 48 error: null 49 } 50 | { 51 success: false 52 value: null 53 error: E 54 } 55> { 56 try { 57 const value = await promise 58 return { success: true, value, error: null } 59 } catch (error) { 60 return { success: false, value: null, error: error as E } 61 } 62} 63 64/** 65 * Fetches data from a URL with caching. 66 * Cache is stored in the KV 67 * @param url - The URL to fetch data from. 68 * @returns A promise that resolves to an object with success status, value, and error. 69 */ 70export async function fetchWithCache<T>(url: string): Promise< 71 | { 72 success: true 73 value: T 74 error: null 75 } 76 | { 77 success: false 78 value: null 79 error: string 80 } 81> { 82 // Calculate the hash of the request URL 83 const keybuffer = new TextEncoder().encode(url) 84 const hashBuffer = await crypto.subtle.digest("SHA-256", keybuffer) 85 const hash = encodeHex(hashBuffer) 86 87 // Check if the data is already in cache 88 const cached = await kv.get<T>(["fetch-cache", hash]) 89 if (cached.value) { 90 console.info("Serving cached request data") 91 return { 92 success: true, 93 value: cached.value, 94 error: null, 95 } 96 } 97 98 // Fetch the data from the URL 99 const data = await tryCatch( 100 fetch(url).then((res) => res.json() as T), 101 ) 102 103 if (!data.success) { 104 return { 105 success: false, 106 value: null, 107 error: data.error.message, 108 } 109 } 110 111 // Store the fetched data in cache, with a 1-day expiration 112 await kv.set(["fetch-cache", hash], data.value, { expireIn: 60 * 60 * 24 }) 113 114 return { 115 success: true, 116 value: data.value, 117 error: null, 118 } 119}