GraphQL Schema

The same data model that powers Seed Export can describe itself in GraphQL:

npx bd export-graphql-schema

That writes ./bd.data-model.graphql (change it with --out path) — SDL generated from your org's custom collections:

  • every object becomes a type, fields typed from their declarations;
  • relations render as lists of the target type;
  • SELECT fields become GraphQL enums where the values are valid enum names;
  • a Query type is emitted with a list field and a by-id field for every object.

Add --include-standard and the platform's standard CRM types are emitted beside your custom ones.

The @businessdash/sdk/graphql entrypoint exposes the generator (dataModelToGraphqlSdl) and its supporting types for programmatic use.

Schema-first, deliberately

The SDL is a contract, and contracts are useful the moment they exist:

  • feed it to GraphQL codegen and get typed clients in any language;
  • commit it next to your code so schema changes show up in code review as a readable diff;
  • give it to an AI agent alongside the seed snapshot — structure from one file, contents from the other.

The command renders a schema. To actually run queries against it, see Executable endpoint below — an enterprise add-on. Everyday reads and writes still go through the Package API and the SDK clients.

Scope

export-graphql-schema requires metadata:read_schema on a secret key — schema only, so metadata:read_records is not needed.

Executable endpoint

Beyond generating the SDL, an org on the top (enterprise) plan can run GraphQL against its custom data model:

POST https://www.businessdash.us/api/package/v1/sites/{siteId}/graphql
Authorization: Bearer bd_…
Content-Type: application/json

{ "query": "{ projects(limit: 20) { nodes { id title tasks { id name } } nextCursor } }" }

The response is the standard GraphQL envelope — { "data": … }, or { "errors": [ … ] } for a bad query (a malformed query is a GraphQL error, not an HTTP failure). Every relation is a list; each object exposes a <plural>(limit, cursor) connection and a <singular>(id) field.

The @businessdash/sdk/graphql entrypoint ships a tiny client so you don't hand-roll the fetch:

import { createGraphqlClient } from "@businessdash/sdk/graphql";

const gql = createGraphqlClient({
  baseUrl: "https://www.businessdash.us/api/package/v1",
  siteId: process.env.BD_SITE_ID!,
  apiKey: process.env.BD_API_KEY!, // secret key with graphql:query
});

const { data, errors } = await gql.query(
  `{ projects(limit: 20) { nodes { id title } } }`,
);

Access & limits

  • Scope graphql:query — a dedicated, secret-key-only scope (never publishable). Because only a secret key can reach it, a query reads the org's full model with no per-object visibility gate.
  • Plan — requires the developer.graphql_api entitlement (top/enterprise plan). A non-entitled caller gets { "available": false, "reason": "plan_required" }. Platform-developer keys bypass the plan gate.
  • Rate limit — each key has a per-minute ceiling set by BusinessDash as part of your plan (a 429 with a retry hint when exceeded). Platform-developer keys are unlimited.
  • Query depth is capped, and limit is clamped per page, to keep a single query bounded.

Usage & billing

GraphQL usage is metered per month: the number of queries run and the number of records they read, tracked separately from the REST custom-DB counters so the surface can be priced on its own. Your GraphQL usage for the current month is shown in Site Builder → Developer, beside the key that holds graphql:query.