11import type { NextApiRequest , NextApiResponse } from 'next' ;
2-
3- const GRAPHQL_ENDPOINT = 'https://tokenlogic-data.ddn.hasura.app/graphql' ;
4- const API_KEY = process . env . TOKENLOGIC_API_KEY ;
5-
6- type SGhoRatesData = {
7- blockHour : string ;
8- apr : number ;
9- } ;
10-
11- type GraphQLResponse = {
12- data ?: {
13- aaveV3RatesSgho : SGhoRatesData [ ] ;
14- } ;
15- errors ?: Array < {
16- message : string ;
17- locations ?: Array < { line : number ; column : number } > ;
18- path ?: string [ ] ;
19- } > ;
20- } ;
21-
22- type ApiResponse = {
23- data ?: Array < {
24- day : { value : string } ;
25- merit_apy : number ;
26- } > ;
27- error ?: string ;
28- } ;
29-
30- /**
31- * Transform GraphQL data to the format expected by the frontend
32- * Aggregates multiple hourly entries per day to a single daily entry
33- */
34- const transformGraphQLData = ( graphqlData : SGhoRatesData [ ] ) => {
35- const dailyData = new Map < string , { timestamp : Date ; merit_apy : number } > ( ) ;
36-
37- graphqlData . forEach ( ( item ) => {
38- const timestamp = new Date ( item . blockHour ) ;
39- const dateString = timestamp . toISOString ( ) . split ( 'T' ) [ 0 ] ;
40-
41- // Keep the latest entry for each day (or first if no existing entry)
42- const existing = dailyData . get ( dateString ) ;
43- if ( ! existing || timestamp > existing . timestamp ) {
44- dailyData . set ( dateString , {
45- timestamp,
46- merit_apy : item . apr ,
47- } ) ;
48- }
49- } ) ;
50-
51- return Array . from ( dailyData . entries ( ) ) . map ( ( [ dateString , { merit_apy } ] ) => ( {
52- day : {
53- value : dateString ,
54- } ,
55- merit_apy,
56- } ) ) ;
57- } ;
2+ import { fetchSGhoApyData } from 'pages/api/SGhoService' ;
3+ import { ApiResponse } from 'pages/api/SGhoService.types' ;
584
595/**
606 * Next.js API route to fetch sGHO APY data from TokenLogic GraphQL API
@@ -72,107 +18,34 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
7218 }
7319
7420 try {
75- // Check if API key is configured
76- if ( ! API_KEY ) {
77- console . error ( 'TOKENLOGIC_API_KEY environment variable not set' ) ;
78- return res . status ( 500 ) . json ( {
79- error : 'Server configuration error' ,
80- } ) ;
81- }
82-
83- // Parse query parameters
8421 const limit = parseInt ( req . query . limit as string ) || 100 ;
8522 const startDate = req . query . startDate as string ;
8623 const endDate = req . query . endDate as string ;
8724
88- // Build GraphQL query based on parameters
89- let graphqlQuery : string ;
90- let variables : Record < string , string | number > ;
91-
92- if ( startDate && endDate ) {
93- // Query with date range
94- graphqlQuery = `
95- query GetSGhoApyHistoryDateRange($startDate: timestamptz!, $endDate: timestamptz!, $limit: Int!) {
96- aaveV3RatesSgho(
97- limit: $limit,
98- where: {
99- blockHour: {
100- _gte: $startDate,
101- _lte: $endDate
102- }
103- }
104- ) {
105- blockHour
106- apr
107- }
108- }
109- ` ;
110- variables = { startDate, endDate, limit } ;
111- } else {
112- // Query for recent data
113- graphqlQuery = `
114- query GetSGhoApyHistory($limit: Int!) {
115- aaveV3RatesSgho(limit: $limit) {
116- blockHour
117- apr
118- }
119- }
120- ` ;
121- variables = { limit } ;
122- }
123-
124- // Make GraphQL request
125- const response = await fetch ( GRAPHQL_ENDPOINT , {
126- method : 'POST' ,
127- headers : {
128- 'Content-Type' : 'application/json' ,
129- 'x-api-key' : API_KEY ,
130- } ,
131- body : JSON . stringify ( {
132- query : graphqlQuery ,
133- variables,
134- } ) ,
25+ const result = await fetchSGhoApyData ( {
26+ limit,
27+ startDate,
28+ endDate,
13529 } ) ;
13630
137- if ( ! response . ok ) {
138- console . error ( `GraphQL HTTP error: ${ response . status } ` ) ;
139- return res . status ( response . status ) . json ( {
140- error : `Failed to fetch data: ${ response . statusText } ` ,
141- } ) ;
142- }
143-
144- const result : GraphQLResponse = await response . json ( ) ;
31+ res . status ( 200 ) . json ( result ) ;
32+ } catch ( error ) {
33+ console . error ( 'API route error:' , error ) ;
14534
146- // Check for GraphQL errors
147- if ( result . errors && result . errors . length > 0 ) {
148- console . error ( 'GraphQL errors:' , result . errors ) ;
35+ if ( error . message . includes ( 'GraphQL error' ) ) {
14936 return res . status ( 400 ) . json ( {
150- error : `GraphQL error: ${ result . errors . map ( ( e ) => e . message ) . join ( ', ' ) } ` ,
37+ error : error . message ,
15138 } ) ;
15239 }
15340
154- // Validate response structure
155- if ( ! result . data ?. aaveV3RatesSgho || ! Array . isArray ( result . data . aaveV3RatesSgho ) ) {
156- console . error ( 'Invalid GraphQL response format:' , result ) ;
157- return res . status ( 500 ) . json ( {
158- error : 'Invalid response format from data source' ,
41+ if ( error . message . includes ( 'HTTP error' ) ) {
42+ return res . status ( 502 ) . json ( {
43+ error : 'Failed to fetch data from external service' ,
15944 } ) ;
16045 }
161-
162- const transformedData = transformGraphQLData ( result . data . aaveV3RatesSgho ) ;
163-
164- const sortedData = transformedData . sort ( ( a , b ) => {
165- const dateA = new Date ( a . day . value ) ;
166- const dateB = new Date ( b . day . value ) ;
167- return dateA . getTime ( ) - dateB . getTime ( ) ;
168- } ) ;
169-
170- // Return successful response
171- res . status ( 200 ) . json ( { data : sortedData } ) ;
172- } catch ( error ) {
173- console . error ( 'API route error:' , error ) ;
174- res . status ( 500 ) . json ( {
175- error : 'Internal server error' ,
176- } ) ;
17746 }
47+
48+ res . status ( 500 ) . json ( {
49+ error : 'Internal server error' ,
50+ } ) ;
17851}
0 commit comments