Auto-indexing service and GraphQL API for AT Protocol Records quickslice.slices.network/
atproto gleam graphql
at main 58 lines 1.5 kB view raw
1import { createDPoPProof } from './auth/dpop'; 2import { getValidAccessToken } from './auth/tokens'; 3import { Storage } from './storage/storage'; 4 5export interface GraphQLResponse<T = unknown> { 6 data?: T; 7 errors?: Array<{ message: string; path?: string[] }>; 8} 9 10/** 11 * Execute a GraphQL query or mutation 12 */ 13export async function graphqlRequest<T = unknown>( 14 storage: Storage, 15 namespace: string, 16 graphqlUrl: string, 17 tokenUrl: string, 18 query: string, 19 variables: Record<string, unknown> = {}, 20 requireAuth = false, 21 signal?: AbortSignal 22): Promise<T> { 23 const headers: Record<string, string> = { 24 'Content-Type': 'application/json', 25 }; 26 27 if (requireAuth) { 28 const token = await getValidAccessToken(storage, namespace, tokenUrl); 29 if (!token) { 30 throw new Error('Not authenticated'); 31 } 32 33 // Create DPoP proof bound to this request 34 const dpopProof = await createDPoPProof(namespace, 'POST', graphqlUrl, token); 35 36 headers['Authorization'] = `DPoP ${token}`; 37 headers['DPoP'] = dpopProof; 38 } 39 40 const response = await fetch(graphqlUrl, { 41 method: 'POST', 42 headers, 43 body: JSON.stringify({ query, variables }), 44 signal, 45 }); 46 47 if (!response.ok) { 48 throw new Error(`GraphQL request failed: ${response.statusText}`); 49 } 50 51 const result: GraphQLResponse<T> = await response.json(); 52 53 if (result.errors && result.errors.length > 0) { 54 throw new Error(`GraphQL error: ${result.errors[0].message}`); 55 } 56 57 return result.data as T; 58}