Appearance
A data model in the middle
You have data. Some of it lives in spreadsheets. Some sits behind a SaaS API — Stripe, HubSpot, your CRM, whatever else the team has accumulated. You want to ask questions of it and see the answers in a dashboard. The question is whether there should be something in between the sources and the dashboard — a data model, built by Claude, that the dashboard talks to instead of the sources directly.
Short answer: yes, once you have more than one source or more than one question. Below that, skip it.
The two shapes
| Shape | What it looks like |
|---|---|
| Direct | Dashboard reads the spreadsheet or hits the API on each load. No middle layer. |
| Modelled | A scheduled job pulls each source into a local database. The dashboard reads the database. |
For one sheet and one chart, the direct shape is genuinely the simplest thing that works. Adding a database adds a moving part for no current benefit.
For two sources you want to join, or three questions that pull on overlapping data, direct stops being simple. Every chart re-pays the cost of fetching, normalising, joining, and resolving identities. The middle layer is where you pay that cost once.
Why a middle layer earns its place
Joins need shared identity. "Customer X" in your CRM has a different ID from "Customer X" in Stripe. Resolving that to one entity is work. Without a model, you do it inside every chart's query. With a model, you do it once at ingestion.
Questions stabilise faster than sources. "Show me revenue per region this quarter" is the same question whether your billing lives in Stripe today or Chargebee next year. A model lets the source change without breaking the question.
Schema is documentation. Once Claude has built customers, orders, line_items, you can hand the schema to the next agent — or the next person — and they know what's available without re-reading the source APIs.
SQL is the universal dashboard input. Every charting tool — Grafana, Metabase, Observable, a Next.js page with shadcn — speaks SQL. Put data in a SQL store and your dashboard options stay open.
Refresh becomes a separate concern. "Pull from source" and "answer the question" become two jobs. The pull job can fail at 3am without taking the dashboard down. Yesterday's data is better than no data.
LLM-written queries get dramatically better. If you want to "ask questions in plain English and see the answer", an LLM writing SQL against your normalised local schema is night-and-day better than the same LLM trying to compose joins across three heterogeneous APIs. The model is what makes the chat interface trustworthy.
Why this is a Claude-shaped problem
Schema design from sample data — here are three CSVs and one API response, what's the minimum normalised schema that holds them? — is exactly the kind of structured reasoning task Claude handles well. So is writing the Drizzle or Prisma migrations, the ingestion scripts, and the SQL behind each chart.
The loop is short:
- You describe a question
- Claude extends the schema if it needs to
- Claude writes the ingestion and the query
- The chart updates
- You ask the next question
The model grows in response to questions, not in advance of them. The schema you'd design up-front is a guess; the schema you grow per question is a record of what you actually needed.
The simplest shape that works
For a first cut, all you need:
| Layer | Tool |
|---|---|
| Database | Local SQLite file. No server, no DBaaS bill, no auth. Just a file on disk. |
| ORM | Drizzle (lighter, schema-first) or Prisma (heavier, migration-first). Either works — Drizzle reads more naturally for Claude. |
| Ingestion | One TypeScript script per source. Read the sheet, hit the API, upsert into the model. |
| Dashboard | Whatever you already use — a Next.js page, a Streamlit app, or Metabase pointed straight at the SQLite file. |
Drizzle + SQLite + a Next.js app is roughly the Stride starter app shape with the database swapped for a local file. You can grow into Postgres on Neon the day SQLite stops being enough — which, for internal-tool data volumes, is rarely day one.
The order that matters
The trap with a middle layer is modelling first. You sit down with the source data, design "the right schema", and find that none of the questions you actually want to ask map cleanly onto it. You've decided in advance of needing to.
The order that works:
- Question first. Pick the question whose answer would change a decision this week.
- Sources next. Identify the smallest set of data that could answer it.
- Model last, just enough. Add the tables and columns that question needs. Nothing more.
Then the next question. Then the next. The model accretes around real questions, not imagined ones. Every other table is a guess.
This is You Ain't Gonna Need It ("YAGNI") applied to schemas: an unused column is not foresight, it's clutter the next agent has to reason about.
When to skip the middle layer
The middle layer isn't free. Skip it when:
- One source, one dashboard — your spreadsheet is your model. A
SELECTover the sheet is cheaper than building infra around it. - Data is already in a queryable store — if it's all in Postgres already, point the dashboard at Postgres.
- The question is genuinely one-off — for "what does this look like once?", a notebook beats infrastructure.
- The data is too big for SQLite — past tens of millions of rows, or with concurrent writers, SQLite stops being the simple choice. Reach for DuckDB (for analytics) or Postgres (for everything else).
Where this breaks
| Risk | What it looks like |
|---|---|
| Stale data | The dashboard says one thing, the source says another. Need a clear refresh story — schedule, last-updated stamps, visible freshness on the chart. |
| Schema drift | Source API adds a field, your model doesn't know. Need a pass that checks source shape against the model on each pull. |
| Identity resolution gets hard | Two systems disagree about who a customer is. Need a deliberate entities table and a way to merge. |
| The junk-drawer model | After thirty questions the schema is a mess. Periodically ask Claude to propose a refactor — small-classes thinking applies to schemas too. |
| Compliance | A SQLite file on a laptop is fine for invoice line items. It's not fine for personal data, regulated records, or anything your DPO would want to know about. Decide what can leave the source before you start ingesting. |
None of these are reasons to skip the model. They're reasons to keep it deliberately small.
So — is it a good idea?
| Situation | Middle layer? |
|---|---|
| One source, one chart | No. Direct. |
| One source, many questions | Probably not yet. Wait for the join. |
| Multiple sources, joined | Yes. The model is the only sane place to do that work. |
| Multiple sources, many questions | Yes — and the model is the actual app, the dashboard is just one view of it. |
For the Stride audience — non-developer in finance, ops, marketing, research, joining a SaaS API and a couple of spreadsheets to answer a question someone asked in a meeting — the answer almost always lands on yes, with SQLite and an ORM, grown one question at a time.
That's the simplest thing that actually works for the problem you're solving.
Further reading
- Agentic engineering — why the model needs traceable history, not just working code
- Can't we just use Lovable? — the same shape of question, one rung up: when does the lightweight tool earn graduation to the structured one?