@nvana-price/price-service-client
v1.1.2
Published
TypeScript client library for the Price Service API. Auto-generated from OpenAPI spec using [@hey-api/openapi-ts](https://heyapi.dev/).
Readme
@nvana-price/price-service-client
TypeScript client library for the Price Service API. Auto-generated from OpenAPI spec using @hey-api/openapi-ts.
Installation
This is a workspace package. Install dependencies from the monorepo root:
pnpm installQuick Start
Basic Usage
import { createClient, getLatestPrices, getPricesAtTime, getSymbols } from '@nvana-price/price-service-client'
// Create a client instance
const client = createClient({
baseUrl: 'https://your-price-service.com/api'
})
// Get latest prices for multiple symbols
const { data, error } = await getLatestPrices({
client,
query: {
symbol: ['BTC', 'ETH', 'USDC']
}
})
if (error) {
console.error('Failed to fetch prices:', error)
} else {
console.log('Prices:', data.prices)
}Without Client Instance
You can also call functions without creating a client (uses default baseUrl):
import { getLatestPrices } from '@nvana-price/price-service-client'
const { data } = await getLatestPrices({
query: {
symbol: ['BTC', 'ETH']
}
})API Reference
Client Creation
createClient(config)
Creates a configured client instance for making API requests.
Parameters:
baseUrl(string, required) - Base URL of the Price Service APIheaders(object, optional) - Default headers for all requestsfetch(function, optional) - Custom fetch implementation
Example:
import { createClient } from '@nvana-price/price-service-client'
const client = createClient({
baseUrl: 'https://price-service.example.com/api',
headers: {
'X-Custom-Header': 'value'
}
})API Methods
getLatestPrices(options)
Get the latest prices for one or more symbols.
Parameters:
{
client?: Client // Optional client instance
query: {
symbol: string[] // Array of symbols (e.g., ['BTC', 'ETH'])
}
}Returns:
{
data?: {
prices: Array<
| { symbol: string; priceUsd: number; unixTimestampSeconds: number }
| { symbol: string; error: 'SYMBOL_NOT_FOUND' | 'NO_PRICE_DATA' }
>
}
error?: unknown
}Example:
const { data, error } = await getLatestPrices({
client,
query: {
symbol: ['BTC', 'ETH', 'USDC', 'INVALID']
}
})
// Response structure:
// {
// prices: [
// { symbol: 'BTC', priceUsd: 45000.50, unixTimestampSeconds: 1699564800 },
// { symbol: 'ETH', priceUsd: 2300.25, unixTimestampSeconds: 1699564800 },
// { symbol: 'USDC', priceUsd: 1.00, unixTimestampSeconds: 1699564800 },
// { symbol: 'INVALID', error: 'SYMBOL_NOT_FOUND' }
// ]
// }getPricesAtTime(options)
Get prices for symbols at a specific point in time.
Parameters:
{
client?: Client // Optional client instance
query: {
symbol: string[] // Array of symbols
timestamp: number // UNIX timestamp in seconds
}
}Returns:
Same structure as getLatestPrices
Example:
// Get prices as of Nov 9, 2023 at 12:00:00 GMT
const timestamp = 1699531200
const { data } = await getPricesAtTime({
client,
query: {
symbol: ['BTC', 'ETH'],
timestamp
}
})
console.log(data.prices)
// [
// { symbol: 'BTC', priceUsd: 35000.00, unixTimestampSeconds: 1699531200 },
// { symbol: 'ETH', priceUsd: 1800.00, unixTimestampSeconds: 1699531200 }
// ]getSymbols(options?)
Get list of all supported symbols with metadata.
Parameters:
{
client?: Client // Optional client instance
}Returns:
{
data?: {
symbols: Array<{
symbol: string
// ... additional metadata fields
}>
}
error?: unknown
}Example:
const { data } = await getSymbols({ client })
console.log(data.symbols)
// [
// { symbol: 'BTC', name: 'Bitcoin', ... },
// { symbol: 'ETH', name: 'Ethereum', ... },
// { symbol: 'USDC', name: 'USD Coin', ... }
// ]Usage Examples
Error Handling
import { getLatestPrices } from '@nvana-price/price-service-client'
const { data, error } = await getLatestPrices({
client,
query: { symbol: ['BTC'] }
})
if (error) {
console.error('API Error:', error)
return
}
// Handle per-symbol errors
for (const price of data.prices) {
if ('error' in price) {
console.log(`Symbol ${price.symbol}: ${price.error}`)
} else {
console.log(`${price.symbol}: $${price.priceUsd}`)
}
}With Custom Fetch
import { createClient, getLatestPrices } from '@nvana-price/price-service-client'
const client = createClient({
baseUrl: 'https://price-service.example.com/api',
fetch: async (url, init) => {
// Add custom logic (logging, retries, etc.)
console.log('Fetching:', url)
return fetch(url, init)
}
})
const { data } = await getLatestPrices({
client,
query: { symbol: ['BTC'] }
})Batch Price Fetching
import { createClient, getLatestPrices } from '@nvana-price/price-service-client'
async function getPricesForPortfolio(symbols: string[]) {
const client = createClient({
baseUrl: process.env.PRICE_SERVICE_URL
})
const { data, error } = await getLatestPrices({
client,
query: { symbol: symbols }
})
if (error) {
throw new Error(`Failed to fetch prices: ${error}`)
}
// Convert to map for easy lookup
const priceMap = new Map<string, number>()
for (const item of data.prices) {
if ('priceUsd' in item) {
priceMap.set(item.symbol, item.priceUsd)
}
}
return priceMap
}
// Usage
const prices = await getPricesForPortfolio(['BTC', 'ETH', 'USDC'])
console.log('BTC Price:', prices.get('BTC'))Historical Price Analysis
import { createClient, getPricesAtTime } from '@nvana-price/price-service-client'
const client = createClient({
baseUrl: 'https://price-service.example.com/api'
})
async function getPriceHistory(symbol: string, timestamps: number[]) {
const results = await Promise.all(
timestamps.map(timestamp =>
getPricesAtTime({
client,
query: { symbol: [symbol], timestamp }
})
)
)
return results
.map((r, i) => {
const price = r.data?.prices[0]
if (price && 'priceUsd' in price) {
return { timestamp: timestamps[i], price: price.priceUsd }
}
return null
})
.filter(Boolean)
}
// Get hourly prices for last 24 hours
const now = Math.floor(Date.now() / 1000)
const timestamps = Array.from({ length: 24 }, (_, i) => now - (i * 3600))
const history = await getPriceHistory('BTC', timestamps)
console.log(history)React Hook Example
import { useEffect, useState } from 'react'
import { createClient, getLatestPrices } from '@nvana-price/price-service-client'
const client = createClient({
baseUrl: 'https://price-service.example.com/api'
})
export function usePrices(symbols: string[]) {
const [prices, setPrices] = useState<Record<string, number>>({})
const [loading, setLoading] = useState(true)
const [error, setError] = useState<Error | null>(null)
useEffect(() => {
async function fetchPrices() {
try {
setLoading(true)
const { data, error } = await getLatestPrices({
client,
query: { symbol: symbols }
})
if (error) {
throw new Error(String(error))
}
const priceMap: Record<string, number> = {}
for (const item of data.prices) {
if ('priceUsd' in item) {
priceMap[item.symbol] = item.priceUsd
}
}
setPrices(priceMap)
setError(null)
} catch (err) {
setError(err instanceof Error ? err : new Error(String(err)))
} finally {
setLoading(false)
}
}
fetchPrices()
}, [symbols.join(',')])
return { prices, loading, error }
}
// Usage in component
function PriceDisplay() {
const { prices, loading, error } = usePrices(['BTC', 'ETH'])
if (loading) return <div>Loading...</div>
if (error) return <div>Error: {error.message}</div>
return (
<div>
<div>BTC: ${prices.BTC?.toFixed(2)}</div>
<div>ETH: ${prices.ETH?.toFixed(2)}</div>
</div>
)
}Regenerating the Client
The client is automatically generated from the Price Service OpenAPI specification.
When to Regenerate
Regenerate when:
- API endpoints change
- Request/response types change
- New endpoints are added
How to Regenerate
# From the price-service package directory
cd ts/price-service
# Generate OpenAPI spec and client in one step
pnpm gen-server && pnpm gen-client
# Or separately:
pnpm gen-server # Generates swagger.json
pnpm gen-client # Generates client from swagger.jsonGeneration command:
openapi-ts -i ./src/swagger.json -o ../price-service-client/src/ -c @hey-api/client-fetchThis uses @hey-api/openapi-ts to generate the client code.
Type Safety
The client provides full TypeScript type safety:
import { getLatestPrices } from '@nvana-price/price-service-client'
// ✅ Correct usage
const { data } = await getLatestPrices({
query: { symbol: ['BTC'] }
})
// ❌ TypeScript error - symbol must be an array
const { data } = await getLatestPrices({
query: { symbol: 'BTC' } // Error!
})
// ✅ Type-safe response handling
if (data) {
for (const price of data.prices) {
if ('priceUsd' in price) {
// TypeScript knows this is PriceItemSuccess
console.log(price.priceUsd)
} else {
// TypeScript knows this is PriceItemError
console.log(price.error)
}
}
}Configuration
Environment Variables
# Base URL for the Price Service
PRICE_SERVICE_URL=https://your-price-service.com/apiClient Options
import { createClient } from '@nvana-price/price-service-client'
const client = createClient({
// Base URL (required)
baseUrl: 'https://price-service.example.com/api',
// Custom headers (optional)
headers: {
'Authorization': 'Bearer token',
'X-Custom-Header': 'value'
},
// Custom fetch (optional)
fetch: customFetch,
// Query serializer options (optional)
querySerializer: {
array: {
style: 'form', // or 'spaceDelimited', 'pipeDelimited'
explode: true // ?symbol=BTC&symbol=ETH vs ?symbol=BTC,ETH
}
}
})Testing
import { createClient, getLatestPrices } from '@nvana-price/price-service-client'
// Mock fetch for testing
const mockFetch = jest.fn().mockResolvedValue({
ok: true,
json: async () => ({
prices: [
{ symbol: 'BTC', priceUsd: 45000, unixTimestampSeconds: 1699564800 }
]
})
})
const client = createClient({
baseUrl: 'http://test.example.com',
fetch: mockFetch
})
test('fetches prices', async () => {
const { data } = await getLatestPrices({
client,
query: { symbol: ['BTC'] }
})
expect(data?.prices).toHaveLength(1)
expect(data?.prices[0]).toMatchObject({
symbol: 'BTC',
priceUsd: 45000
})
})Troubleshooting
BaseURL not set
Error: baseUrl is requiredSolution: Create a client with baseUrl or set a default:
const client = createClient({
baseUrl: process.env.PRICE_SERVICE_URL || 'https://default-url.com/api'
})CORS Errors
If calling from browser and getting CORS errors, ensure the Price Service is configured to allow your origin.
Type Errors After Regeneration
After regenerating the client:
- Clean build artifacts:
rm -rf build/ - Rebuild:
pnpm build - Restart TypeScript server in your editor
Related Packages
@nvana-price/price-service- The Price Service API server@nvana-price/db-gen- Database client
License
UNLICENSED
