Skip to content
This repository was archived by the owner on Jul 3, 2026. It is now read-only.

Latest commit

 

History

2 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Important

This repository is archived and no longer maintained. It was part of an earlier version of Bonnard. Our current products live at bonnard.dev. Start with @bonnard/mcp-charts. Questions: pierre@bonnard.dev

@bonnard/sdk

TypeScript SDK for querying the Bonnard semantic layer from any JavaScript or TypeScript application.

Install

npm install @bonnard/sdk

Quick Start

With a publishable key

import { createClient } from '@bonnard/sdk';

const bon = createClient({
  apiKey: 'bon_pk_...',
});

const { data } = await bon.query({
  measures: ['orders.revenue', 'orders.count'],
  dimensions: ['orders.status'],
});

console.log(data);
// [{ "orders.revenue": 45000, "orders.count": 120, "orders.status": "completed" }, ...]

With token exchange (multi-tenant)

For B2B apps where each user sees their own data, use a server-side secret key to mint scoped tokens:

// Server: exchange secret key for a scoped JWT
const res = await fetch('https://app.bonnard.dev/api/sdk/token', {
  method: 'POST',
  headers: {
    Authorization: 'Bearer bon_sk_...',
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    security_context: { tenant_id: 'acme-123' },
  }),
});
const { token } = await res.json();
// Client: use the token callback
const bon = createClient({
  fetchToken: async () => {
    const res = await fetch('/api/analytics/token');
    const { token } = await res.json();
    return token;
  },
});

const { data } = await bon.query({
  measures: ['orders.revenue'],
  timeDimension: {
    dimension: 'orders.created_at',
    granularity: 'month',
    dateRange: ['2025-01-01', '2025-12-31'],
  },
});

Tokens are cached automatically and refreshed 60 seconds before expiry.

API

createClient(config)

Option Type Description
apiKey string Publishable key (bon_pk_...). Use one of apiKey or fetchToken.
fetchToken () => Promise<string> Async callback that returns a JWT. Use for multi-tenant setups.
baseUrl string API base URL (default: https://app.bonnard.dev)

client.query(options)

JSON query against the semantic layer.

const { data } = await bon.query({
  measures: ['orders.revenue', 'orders.count'],
  dimensions: ['orders.product_category'],
  filters: [
    { dimension: 'orders.status', operator: 'equals', values: ['completed'] },
  ],
  timeDimension: {
    dimension: 'orders.created_at',
    granularity: 'month',
    dateRange: ['2025-01-01', '2025-12-31'],
  },
  orderBy: { 'orders.revenue': 'desc' },
  limit: 100,
});

client.sql(query)

Raw SQL query using Cube SQL syntax.

const { data } = await bon.sql(
  `SELECT product_category, MEASURE(revenue) FROM orders GROUP BY 1`
);

Error handling

All API errors throw BonnardError with the HTTP status code:

import { createClient, BonnardError } from '@bonnard/sdk';

try {
  const { data } = await bon.query({ measures: ['orders.revenue'] });
} catch (err) {
  if (err instanceof BonnardError) {
    console.log(err.statusCode); // 401, 400, 500, etc.
    console.log(err.retryable);  // true for 429 and 5xx
  }
}

Links

License

MIT