コンテンツにスキップ

Client Setup Guides

このコンテンツはまだ日本語訳がありません。

Set up GraphQL clients in various languages to interact with Sai Keeper API.


Apollo Client is the most popular GraphQL client for JavaScript applications.

Terminal window
npm install @apollo/client graphql
# For subscriptions
npm install graphql-ws
import { ApolloClient, InMemoryCache, HttpLink, gql } from '@apollo/client'
const client = new ApolloClient({
link: new HttpLink({
uri: 'https://sai-keeper.testnet-2.nibiru.fi/graphql'
}),
cache: new InMemoryCache()
})
// Example query
const GET_PRICES = gql`
query GetPrices {
oracle {
tokenPricesUsd(limit: 10) {
token { symbol }
priceUsd
}
}
}
`
async function fetchPrices() {
const { data, error } = await client.query({
query: GET_PRICES
})
if (error) {
console.error('Error:', error)
return
}
console.log('Prices:', data.oracle.tokenPricesUsd)
}
fetchPrices()
import {
ApolloClient,
InMemoryCache,
HttpLink,
split,
gql
} from '@apollo/client'
import { GraphQLWsLink } from '@apollo/client/link/subscriptions'
import { getMainDefinition } from '@apollo/client/utilities'
import { createClient } from 'graphql-ws'
// HTTP link for queries and mutations
const httpLink = new HttpLink({
uri: 'https://sai-keeper.testnet-2.nibiru.fi/graphql'
})
// WebSocket link for subscriptions
const wsLink = new GraphQLWsLink(
createClient({
url: 'wss://sai-keeper.testnet-2.nibiru.fi/graphql',
connectionParams: {
// Add authentication if needed
// authToken: 'your-auth-token'
},
retryAttempts: 5,
shouldRetry: () => true,
on: {
connected: () => console.log('WebSocket connected'),
closed: () => console.log('WebSocket closed'),
error: (error) => console.error('WebSocket error:', error)
}
})
)
// Split traffic between HTTP and WebSocket
const splitLink = split(
({ query }) => {
const definition = getMainDefinition(query)
return (
definition.kind === 'OperationDefinition' &&
definition.operation === 'subscription'
)
},
wsLink,
httpLink
)
const client = new ApolloClient({
link: splitLink,
cache: new InMemoryCache(),
defaultOptions: {
watchQuery: {
fetchPolicy: 'cache-and-network'
}
}
})
// Example subscription
const WATCH_PRICES = gql`
subscription WatchPrices {
tokenPricesUsd {
token { symbol }
priceUsd
}
}
`
const subscription = client.subscribe({
query: WATCH_PRICES
}).subscribe({
next: ({ data }) => {
console.log('Price update:', data.tokenPricesUsd)
},
error: (error) => {
console.error('Subscription error:', error)
}
})
// Clean up
// subscription.unsubscribe()
export default client
import { ApolloProvider, useQuery, useSubscription, gql } from '@apollo/client'
import client from './apollo-client'
// Wrap your app
function App() {
return (
<ApolloProvider client={client}>
<Dashboard />
</ApolloProvider>
)
}
// Use queries in components
function Dashboard() {
const GET_TRADES = gql`
query GetTrades($trader: String!) {
perp {
trades(where: { trader: $trader, isOpen: true }) {
id
isLong
leverage
state { pnlPct }
}
}
}
`
const { loading, error, data } = useQuery(GET_TRADES, {
variables: { trader: 'nibi1abc...' }
})
if (loading) return <div>Loading...</div>
if (error) return <div>Error: {error.message}</div>
return (
<div>
{data.perp.trades.map(trade => (
<div key={trade.id}>
Trade #{trade.id} - PnL: {(trade.state.pnlPct * 100).toFixed(2)}%
</div>
))}
</div>
)
}
// Use subscriptions in components
function PriceTicker() {
const WATCH_PRICES = gql`
subscription {
tokenPricesUsd {
token { symbol }
priceUsd
}
}
`
const { data, loading } = useSubscription(WATCH_PRICES)
if (loading) return <div>Connecting...</div>
return (
<div>
{data.tokenPricesUsd.map(item => (
<div key={item.token.symbol}>
{item.token.symbol}: ${item.priceUsd.toFixed(2)}
</div>
))}
</div>
)
}

urql is a lightweight alternative to Apollo Client.

Terminal window
npm install urql graphql
# For subscriptions
npm install graphql-ws
import { createClient, fetchExchange, subscriptionExchange } from 'urql'
import { createClient as createWSClient } from 'graphql-ws'
const wsClient = createWSClient({
url: 'wss://sai-keeper.testnet-2.nibiru.fi/graphql'
})
const client = createClient({
url: 'https://sai-keeper.testnet-2.nibiru.fi/graphql',
exchanges: [
fetchExchange,
subscriptionExchange({
forwardSubscription: (operation) => ({
subscribe: (sink) => ({
unsubscribe: wsClient.subscribe(operation, sink)
})
})
})
]
})
export default client
import { Provider, useQuery, useSubscription } from 'urql'
import client from './urql-client'
function App() {
return (
<Provider value={client}>
<Dashboard />
</Provider>
)
}
function Dashboard() {
const QUERY = `
query GetTrades($trader: String!) {
perp {
trades(where: { trader: $trader, isOpen: true }) {
id
isLong
leverage
}
}
}
`
const [result] = useQuery({
query: QUERY,
variables: { trader: 'nibi1abc...' }
})
const { data, fetching, error } = result
if (fetching) return <div>Loading...</div>
if (error) return <div>Error: {error.message}</div>
return (
<div>
{data.perp.trades.map(trade => (
<div key={trade.id}>Trade #{trade.id}</div>
))}
</div>
)
}

Lightweight library for simple queries (no caching or subscriptions).

Terminal window
npm install graphql-request graphql
import { GraphQLClient, gql } from 'graphql-request'
const client = new GraphQLClient(
'https://sai-keeper.testnet-2.nibiru.fi/graphql'
)
const GET_TRADES = gql`
query GetTrades($trader: String!) {
perp {
trades(where: { trader: $trader, isOpen: true }) {
id
isLong
leverage
}
}
}
`
async function fetchTrades(trader: string) {
try {
const data = await client.request(GET_TRADES, { trader })
console.log('Trades:', data.perp.trades)
return data.perp.trades
} catch (error) {
console.error('Error fetching trades:', error)
throw error
}
}
fetchTrades('nibi1abc...')

Full-featured GraphQL client for Python.

Terminal window
pip install gql[all]
from gql import gql, Client
from gql.transport.aiohttp import AIOHTTPTransport
from gql.transport.websockets import WebsocketsTransport
import asyncio
# HTTP Transport for queries
http_transport = AIOHTTPTransport(
url="https://sai-keeper.testnet-2.nibiru.fi/graphql"
)
# Create client
client = Client(
transport=http_transport,
fetch_schema_from_transport=True
)
# Example query
async def get_trades(trader_address):
query = gql("""
query GetTrades($trader: String!) {
perp {
trades(where: { trader: $trader, isOpen: true }) {
id
isLong
leverage
state {
pnlPct
liquidationPrice
}
perpBorrowing {
baseToken {
symbol
}
}
}
}
}
""")
params = {"trader": trader_address}
async with Client(transport=http_transport) as session:
result = await session.execute(query, variable_values=params)
return result['perp']['trades']
# Run the query
trades = asyncio.run(get_trades("nibi1abc..."))
for trade in trades:
pnl_pct = trade['state']['pnlPct'] * 100
print(f"Trade #{trade['id']}: {trade['perpBorrowing']['baseToken']['symbol']} {trade['leverage']}x - PnL: {pnl_pct:.2f}%")
from gql import gql, Client
from gql.transport.websockets import WebsocketsTransport
import asyncio
# WebSocket transport for subscriptions
ws_transport = WebsocketsTransport(
url="wss://sai-keeper.testnet-2.nibiru.fi/graphql"
)
async def watch_prices():
subscription = gql("""
subscription WatchPrices {
tokenPricesUsd {
token {
symbol
}
priceUsd
}
}
""")
async with Client(transport=ws_transport) as session:
async for result in session.subscribe(subscription):
prices = result['tokenPricesUsd']
for item in prices:
print(f"{item['token']['symbol']}: ${item['priceUsd']:.2f}")
# Run subscription
asyncio.run(watch_prices())
from gql import gql, Client
from gql.transport.requests import RequestsHTTPTransport
# Synchronous transport
transport = RequestsHTTPTransport(
url="https://sai-keeper.testnet-2.nibiru.fi/graphql",
verify=True,
retries=3
)
client = Client(transport=transport, fetch_schema_from_transport=True)
# Synchronous query
def get_token_prices():
query = gql("""
query GetPrices {
oracle {
tokenPricesUsd(limit: 10) {
token {
symbol
}
priceUsd
}
}
}
""")
result = client.execute(query)
return result['oracle']['tokenPricesUsd']
prices = get_token_prices()
for item in prices:
print(f"{item['token']['symbol']}: ${item['priceUsd']:.2f}")

Type-safe GraphQL client for Rust.

Add to Cargo.toml:

[dependencies]
graphql_client = "0.13"
reqwest = { version = "0.11", features = ["json"] }
tokio = { version = "1", features = ["full"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
use graphql_client::{GraphQLQuery, Response};
use reqwest;
// Define the query
#[derive(GraphQLQuery)]
#[graphql(
schema_path = "schema.graphql",
query_path = "queries/get_trades.graphql",
response_derives = "Debug"
)]
pub struct GetTrades;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let client = reqwest::Client::new();
let variables = get_trades::Variables {
trader: "nibi1abc...".to_string(),
};
let request_body = GetTrades::build_query(variables);
let res = client
.post("https://sai-keeper.testnet-2.nibiru.fi/graphql")
.json(&request_body)
.send()
.await?;
let response_body: Response<get_trades::ResponseData> = res.json().await?;
if let Some(data) = response_body.data {
for trade in data.perp.trades {
println!("Trade #{}: {}x leverage", trade.id, trade.leverage);
}
}
if let Some(errors) = response_body.errors {
for error in errors {
eprintln!("Error: {}", error.message);
}
}
Ok(())
}
use reqwest;
use serde::{Deserialize, Serialize};
use serde_json::json;
#[derive(Debug, Serialize, Deserialize)]
struct GraphQLRequest {
query: String,
variables: serde_json::Value,
}
#[derive(Debug, Deserialize)]
struct GraphQLResponse<T> {
data: Option<T>,
errors: Option<Vec<GraphQLError>>,
}
#[derive(Debug, Deserialize)]
struct GraphQLError {
message: String,
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let client = reqwest::Client::new();
let query = r#"
query GetTrades($trader: String!) {
perp {
trades(where: { trader: $trader, isOpen: true }) {
id
isLong
leverage
}
}
}
"#;
let variables = json!({
"trader": "nibi1abc..."
});
let request = GraphQLRequest {
query: query.to_string(),
variables,
};
let response = client
.post("https://sai-keeper.testnet-2.nibiru.fi/graphql")
.json(&request)
.send()
.await?
.json::<serde_json::Value>()
.await?;
println!("Response: {:#?}", response);
Ok(())
}

Terminal window
go get github.com/machinebox/graphql
package main
import (
"context"
"fmt"
"log"
"github.com/machinebox/graphql"
)
type Trade struct {
ID int `json:"id"`
IsLong bool `json:"isLong"`
Leverage float64 `json:"leverage"`
}
type PerpResponse struct {
Trades []Trade `json:"trades"`
}
type Response struct {
Perp PerpResponse `json:"perp"`
}
func main() {
// Create client
client := graphql.NewClient("https://sai-keeper.testnet-2.nibiru.fi/graphql")
// Define query
req := graphql.NewRequest(`
query GetTrades($trader: String!) {
perp {
trades(where: { trader: $trader, isOpen: true }) {
id
isLong
leverage
}
}
}
`)
// Set variables
req.Var("trader", "nibi1abc...")
// Execute query
ctx := context.Background()
var response Response
if err := client.Run(ctx, req, &response); err != nil {
log.Fatal(err)
}
// Process response
for _, trade := range response.Perp.Trades {
fmt.Printf("Trade #%d: %fx leverage\n", trade.ID, trade.Leverage)
}
}
package main
import (
"context"
"fmt"
"log"
"time"
"github.com/machinebox/graphql"
)
func fetchTrades(trader string) error {
client := graphql.NewClient("https://sai-keeper.testnet-2.nibiru.fi/graphql")
req := graphql.NewRequest(`
query GetTrades($trader: String!) {
perp {
trades(where: { trader: $trader, isOpen: true }) {
id
isLong
leverage
}
}
}
`)
req.Var("trader", trader)
// Add timeout
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
var response map[string]interface{}
if err := client.Run(ctx, req, &response); err != nil {
return fmt.Errorf("query failed: %w", err)
}
// Check for data
perp, ok := response["perp"].(map[string]interface{})
if !ok {
return fmt.Errorf("invalid response structure")
}
trades, ok := perp["trades"].([]interface{})
if !ok {
return fmt.Errorf("invalid trades structure")
}
fmt.Printf("Found %d open trades\n", len(trades))
return nil
}
func main() {
if err := fetchTrades("nibi1abc..."); err != nil {
log.Fatal(err)
}
}

For all languages, consider using environment variables for configuration:

Terminal window
# .env file
SAI_KEEPER_HTTP_URL=https://sai-keeper.testnet-2.nibiru.fi/graphql
SAI_KEEPER_WS_URL=wss://sai-keeper.testnet-2.nibiru.fi/graphql

JavaScript:

const httpUrl = process.env.SAI_KEEPER_HTTP_URL
const wsUrl = process.env.SAI_KEEPER_WS_URL

Python:

import os
http_url = os.getenv('SAI_KEEPER_HTTP_URL')
ws_url = os.getenv('SAI_KEEPER_WS_URL')

Rust:

use std::env;
let http_url = env::var("SAI_KEEPER_HTTP_URL").unwrap();

Go:

import "os"
httpUrl := os.Getenv("SAI_KEEPER_HTTP_URL")