Skip to content

Data

Data contexts

A versioned schema layer that sits between data sources and reports - with drafts, edit locks, and immutable published versions.

A data context is the GraphQL schema a report queries against. It sits above one or more data sources and decides exactly which types and fields are exposed, what they're called, and how they map to the underlying systems. Report authors write GraphQL against the context; they never see the data source directly. It's the contract between data engineers and report authors.

This is one of the most architecturally interesting parts of Golden Reports, because it solves a problem most reporting tools just punt on: how do you let report authors evolve their reports without breaking when the underlying database changes? The answer is drafts and immutable versions with edit locks.


The lifecycle

A data context has three runtime states:

  • A draft is the working copy. Edits go here. A draft is mutable but acquires an edit lock the first time someone edits it - see below.
  • A version is the result of publishing a draft. Versions are immutable. Once a report pins to version 3, version 3 will always look the same; you can't retroactively edit a published schema.
  • Editing an existing context creates a new draft from the latest published version. You can have at most one live draft per context.

Drafts and edit locks

Editing a draft acquires an edit lock under the editor's user. Other users can open the draft read-only while it's held; only the lock holder can save changes.

This isn't a real-time collaborative editing experience - it's intentionally simpler. Schema editing happens in short, focused sessions; the lock prevents two engineers from trampling each other's changes when both have the draft open. An admin can release a held lock from the Manager when needed.


Field mappings

A data context exposes a GraphQL schema - types with named, typed fields - to report authors. Each field has:

  • A display name (the GraphQL field name authors write in their query).
  • A source name (the underlying column, JSON path, or CSV header).
  • A GraphQL type - String, Int, Float, Boolean, Date, DateTime, Decimal, Bytes, or JSON, plus custom object types you assemble.
  • A nullability flag.

Fields don't all have to come from the same source. A draft can pull customer_id and customer_name from a Postgres view, and lifetimeValue from a REST endpoint, and present them as a single Customer type to report authors. The data context is the place where "what the author sees" and "what the database actually has" can diverge.

For optimization, the data context also records filter hints, sort hints, and pagination support per field - these are passed down to the data source when it can answer them efficiently (for example, by turning a filter into a database WHERE clause), and applied in-engine otherwise.


Versioning and pinning

When a draft is published it becomes a numbered version (v1, v2, v3, ...). Versions are:

  • Immutable. Republishing edits requires creating a new draft from the latest version.
  • Independently retainable. Old versions stick around until explicitly retired. Reports pinned to them keep working.
  • Reference-counted. The Manager shows how many reports pin each version. You can't delete a version while reports depend on it.

When you publish v2 of a context, existing reports stay on v1 until their author explicitly upgrades them. The author sees a "version upgrade available" affordance in the editor with a diff: which fields were added, renamed, removed, retyped. This is how you migrate reports forward without breaking older ones.


Editing a data context

  1. Manager → Data contexts → Pick a context → Edit. This creates (or attaches you to) a draft and acquires the edit lock.
  2. Add, rename, or remove fields. Bind each to a source path. Adjust types and nullability.
  3. Preview runs a sample query against the source to confirm the mapping works.
  4. Validate. The Manager checks that every reachable field can resolve against the source schema (e.g. the Postgres column actually exists).
  5. Publish. The draft becomes version N+1. The lock is released.

Reports can now pin to the new version.


Multi-source contexts

A single context can reference multiple data sources. Use this when:

  • A logical entity is split across two systems (e.g. customer master in CRM, lifetime value in warehouse).
  • You're transitioning from an old source to a new one and want to fan out reads.
  • A reference dataset (a CSV of country codes) needs to live alongside your operational database.

The engine fetches each source independently and the platform composes the results. Be aware that very wide joins across sources are not the engine's strength - for tight inner-join semantics, do the join in your warehouse and expose a view.


Schema discovery

When you point a data context at a fresh data source, you don't have to type every field by hand. The Manager asks the connector to discover the source's available fields - columns for a database, paths for a REST response, headers for a CSV - and returns their names, types, and nullability. You then pick the fields you want to expose and the Manager pre-fills the rest.


Best practices

  • Treat the context as a published interface. Renaming a field in v2 means every report on v1 keeps working but every report that upgrades to v2 has to fix the rename. Plan field renames as deprecation cycles, not edits.
  • Keep contexts narrow. A "kitchen sink" context with 200 fields is harder to maintain than three focused ones. Authors don't want to scroll through irrelevant fields.
  • Avoid leaking source-specific names. If your database has cust_lkp_id_fk, expose it as customerId. Authors shouldn't know what column it was.
  • Document fields. Every field has a description field; use it. The editor surfaces descriptions in tooltips for authors.

Next

Previous
Data sources