> ## Site Index
> Fetch the site index at: https://www.credibledata.com/llms.txt
> Use this file to discover all available pages before exploring further.

# The GraphQL Lesson BI Never Learned

> GraphQL let developers get their own data. An agent working from a data model can do the same for business users, and the model says what each number means.

GraphQL caught on because it let app developers get the data they needed without asking the server team for every change. BI never found a way to give business users the same leverage.

In 2012, Facebook was rebuilding its iPhone and Android apps, starting with News Feed. The app developers knew exactly what data each view needed, but the server's APIs returned data in a different shape. Getting the right data took extra server code to package it, extra app code to unpack it, and new server work every time a view changed.

GraphQL split the work differently. The server team published a schema, a list of all the data the apps could ask for. App developers wrote their own queries against the schema and asked for exactly the fields they needed. The server still decided what each app was allowed to see. Most changes to a view no longer needed new server work. Adding a field didn't break any app already using the schema, so there was no new API version and no forced update. Each app started using the new field when it was ready.

But GraphQL only helped people who write code. At Amazon, I built a GraphQL API over all the analytics data in the Devices org, so anyone could ask for exactly the data they needed. Almost nobody did. Most of the people who needed that data didn't write queries, so they kept asking engineers to pull it for them.

An agent can write the query for them.

## Dashboards have the same problem

Business users know exactly what they want to see, but they don't write queries and can't build dashboards themselves. So they ask someone who knows the dashboard tool, and they wait.

BI's version of the problem is worse. Each team finds whatever help it can to get its numbers, and each team decides for itself what "revenue" means.

The fix has two parts. An agent builds the dashboard for the person who asked. I made that case in [Dashboards Aren't Dead. WYSIWYG Builders Are.](/blog/posts/dashboards-arent-dead) The agent also builds every dashboard on one data model that defines each number once. Kyle made that case in [The Semantic Layer Is an Interface, Not a Compiler](/blog/posts/semantic-layer-is-an-interface). Together, the two parts give business users what GraphQL gave app developers: a list of the data they can ask for, and a way to ask without waiting on an engineer.

## A dashboard works like a GraphQL app

In our setup, a dashboard is a data app: a plain web page served from a [Malloy](https://github.com/malloydata/malloy) package. Every number on the page comes from one call, `Publisher.query(modelPath, malloyQuery)`, against the data model in the same package. This works like GraphQL in three ways.

First, there's no server code for each dashboard. A GraphQL app doesn't need a new endpoint for each view, and a dashboard doesn't need its own server code. Every chart on every dashboard sends its query through [the same gateway](/blog/posts/inside-the-ai-analytics-engine). When you add a measure to the data model, every dashboard can use the new measure on its next query.

Second, access rules live on the server. A dashboard can send any Malloy query, and the data model's access rules decide what comes back, based on who is viewing the dashboard. So you don't have to trust the dashboard's code. [Curate What Your Agents See](/blog/posts/curate-what-your-agents-see) explains how to set up those rules, and the [access control doc](/docs/how-to/modeling/fine-grained-acls) has the details.

Third, the agent reads the data model first. GraphQL lets developers browse the schema, usually with a tool called GraphiQL. An agent does the same with the data model: before writing a query, the agent reads the model's sources, measures, and descriptions, so it only uses fields that exist. The difference is how often. A developer usually checks the schema once and works from memory after that. An agent checks the data model every time.

## Where a data model goes further

A data model does two things a GraphQL schema can't.

First, a data model can't drift from what it describes. In GraphQL, each field is backed by server code, and that code can stop matching the schema without anyone noticing. In Malloy, there's no separate code behind a field. When a query asks for a measure, Malloy compiles the measure's definition into the SQL it runs.

Second, a data model says what the data means. Here's a GraphQL type:

```graphql
type OrderItem {
  id: ID!
  salePrice: Float!
  returnAmount: Float!
}
```

The type lists three fields and their types. Here's the same data in a Malloy data model:

```malloy
#(doc) Order line items -- one row per product sold
source: order_items is duckdb.table('items.parquet') extend {
  primary_key: id

  join_one:
    #(doc) Customer who placed the order
    customers with customer_id

  #(doc) Revenue, net of returns
  measure: revenue is sale_price.sum() - return_amount.sum()
}
```

The GraphQL type tells you `salePrice` is a number. It can't tell you that revenue is sales minus returns, that each row is one product sold, or how orders connect to customers. That knowledge usually lives in a wiki, in an analyst's head, or nowhere.

The Malloy data model writes that knowledge down, so an agent uses it on every query. GraphQL and Malloy both reject a query for a field that doesn't exist. The data model's joins and measures also prevent mistakes that still run: joining the wrong tables, counting rows twice, or using gross revenue when you meant net. Those mistakes produce wrong numbers, not error messages, and the person reading the dashboard can't tell. In [Kyle's example](/blog/posts/semantic-layer-is-an-interface), the same question comes back as $1,445 when the real answer is $955. Kyle's [Meaning Is the Product](/blog/posts/meaning-is-the-product) makes the longer case.

## Follow-up questions don't need a new dashboard

A dashboard answers the question someone had when it was built. When the number changes, people ask why, and the dashboard doesn't answer that. With a dashboard tool, the new question becomes a new request. With a data model, the new question is just another query.

The agent that built the dashboard can answer the follow-up without changing the dashboard. The agent reads the data model again and writes a new query. That query can use data the dashboard never showed, or break the number down a new way, and still use only the joins the data model defines and the rows that person is allowed to see. The person asking doesn't need to learn Malloy.

## Who gets to ask

GraphQL let app developers get their own data. With an agent writing queries against a data model, business users can do the same: ask for a number, ask why it changed, and get it calculated the way the company defines it.
