Add proxy with cache for coinbase API requests (#226)

This commit is contained in:
ekzyis 2023-01-28 00:20:33 +01:00 committed by GitHub
parent 670f071177
commit 4ab66a67ae
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 66 additions and 32 deletions

View File

@ -11,7 +11,8 @@ import growth from './growth'
import search from './search'
import rewards from './rewards'
import referrals from './referrals'
import price from './price'
import { GraphQLJSONObject } from 'graphql-type-json'
export default [user, item, message, wallet, lnurl, notifications, invite, sub,
upload, growth, search, rewards, referrals, { JSONObject: GraphQLJSONObject }]
upload, growth, search, rewards, referrals, price, { JSONObject: GraphQLJSONObject }]

32
api/resolvers/price.js Normal file
View File

@ -0,0 +1,32 @@
const cache = new Map();
const expiresIn = 30000; // in milliseconds
async function getPrice(fiat) {
fiat ??= 'USD';
if (cache.has(fiat)) {
const { price, createdAt } = cache.get(fiat)
const expired = createdAt + expiresIn < Date.now()
if (!expired) {
return price;
}
}
const url = `https://api.coinbase.com/v2/prices/BTC-${fiat}/spot`;
const price = await fetch(url)
.then((res) => res.json())
.then((body) => parseFloat(body.data.amount))
.catch((err) => {
console.error(err);
return -1;
});
cache.set(fiat, { price, createdAt: Date.now() });
return price;
}
export default {
Query: {
price: async (parent, { fiatCurrency }, ctx) => {
const price = await getPrice(fiatCurrency);
return price;
},
},
};

View File

@ -10,7 +10,7 @@ import { print } from 'graphql'
import lnd from './lnd'
import search from './search'
import { ME } from '../fragments/users'
import { getPrice } from '../components/price'
import { PRICE } from '../fragments/price'
export default async function getSSRApolloClient (req, me = null) {
const session = req && await getSession({ req })
@ -47,7 +47,9 @@ export function getGetServerSideProps (query, variables = null, notFoundFunc, re
query: ME
})
const price = await getPrice(me?.fiatCurrency)
const { data: { price } } = await client.query({
query: PRICE, variables: { fiatCurrency: me?.fiatCurrency }
})
// we want to use client-side cache
if (nodata && query) {

View File

@ -12,6 +12,7 @@ import upload from './upload'
import growth from './growth'
import rewards from './rewards'
import referrals from './referrals'
import price from './price'
const link = gql`
type Query {
@ -28,4 +29,4 @@ const link = gql`
`
export default [link, user, item, message, wallet, lnurl, notifications, invite,
sub, upload, growth, rewards, referrals]
sub, upload, growth, rewards, referrals, price]

7
api/typeDefs/price.js Normal file
View File

@ -0,0 +1,7 @@
import { gql } from 'apollo-server-micro'
export default gql`
extend type Query {
price(fiatCurrency: String): Float
}
`

View File

@ -9,7 +9,7 @@ import styles from '../styles/post.module.css'
import { useLazyQuery, gql, useMutation } from '@apollo/client'
import { useRouter } from 'next/router'
import Link from 'next/link'
import { CURRENCY_SYMBOLS, usePrice } from './price'
import { usePrice } from './price'
import Avatar from './avatar'
import BootstrapForm from 'react-bootstrap/Form'
import Alert from 'react-bootstrap/Alert'
@ -38,9 +38,7 @@ function satsMin2Mo (minute) {
function PriceHint ({ monthly }) {
const me = useMe()
const price = usePrice()
const fiatSymbol = CURRENCY_SYMBOLS[me?.fiatCurrency || 'USD']
const { price, fiatSymbol } = usePrice()
if (!price || !monthly) {
return null

View File

@ -1,13 +1,13 @@
import React, { useContext, useEffect, useState } from 'react'
import { useQuery } from '@apollo/client'
import { Button } from 'react-bootstrap'
import useSWR from 'swr'
import { fixedDecimal } from '../lib/format'
import { useMe } from './me'
const fetcher = url => fetch(url).then(res => res.json()).catch()
import { PRICE } from '../fragments/price'
export const PriceContext = React.createContext({
price: null
price: null,
fiatSymbol: null
})
export const CURRENCY_SYMBOLS = {
@ -20,24 +20,18 @@ export const CURRENCY_SYMBOLS = {
ZAR: 'R '
}
const endpoint = (fiat) => `https://api.coinbase.com/v2/prices/BTC-${fiat ?? 'USD'}/spot`
export async function getPrice (fiat) {
const data = await fetcher(endpoint(fiat))
return data?.data?.amount
export function usePrice () {
return useContext(PriceContext)
}
export function PriceProvider ({ price, children }) {
const me = useMe()
const { data } = useSWR(
endpoint(me?.fiatCurrency),
fetcher,
{
refreshInterval: 30000
})
const fiatCurrency = me?.fiatCurrency;
const { data } = useQuery(PRICE, { variables: { fiatCurrency }, pollInterval: 30000, fetchPolicy: 'cache-and-network' })
const contextValue = {
price: data?.data?.amount || price
price: data?.price || price,
fiatSymbol: CURRENCY_SYMBOLS[fiatCurrency] || '$'
}
return (
@ -47,19 +41,12 @@ export function PriceProvider ({ price, children }) {
)
}
export function usePrice () {
const { price } = useContext(PriceContext)
return price
}
export default function Price () {
const [asSats, setAsSats] = useState(undefined)
useEffect(() => {
setAsSats(localStorage.getItem('asSats'))
}, [])
const price = usePrice()
const me = useMe()
const fiatSymbol = CURRENCY_SYMBOLS[me?.fiatCurrency || 'USD']
const { price, fiatSymbol } = usePrice()
if (!price) return null

6
fragments/price.js Normal file
View File

@ -0,0 +1,6 @@
import { gql } from '@apollo/client'
export const PRICE = gql`
query price($fiatCurrency: String) {
price(fiatCurrency: $fiatCurrency)
}`