The Problem: When Great Tools Have Rough Edges
Cube is a powerful open-source semantic layer that makes it easier to build analytics-heavy applications. It allows you to model your data using YAML or JavaScript, generate dynamic queries and create performant dashboards across various databases. With features tailored for building internal business intelligence tools, it’s a fantastic foundation for teams dealing with complex data structures.
But like many mature tools, Cube isn’t perfect, especially from a developer experience (DX) perspective.
After using it regularly, I kept bumping into two specific and persistent pain points:
- Type safety (or lack thereof) when working with Cube's React integration in TypeScript projects.
- Debugging challenges when trying to understand which Cube queries were firing and how they behaved.
These weren't deal-breakers, but they were constant sources of friction. Enough to make me pause and think: "What if I could smooth this out myself?"
As someone who has followed AI tooling closely, I realized this was a perfect opportunity to try a new workflow: letting AI help me build the tools I wished Cube had.
The Solution: Let AI Build the Tools
I used Claude and Claude Code to develop two internal tools:
- Cube Records — A type-safe and opinionated wrapper around Cube’s React hooks
- Cube Explorer — A Chrome extension that simplifies Cube query debugging
These weren’t theoretical side projects. They became everyday parts of my workflow. And both were built with AI as a hands-on implementation partner.
Tool 1: Cube Records
What It Is
Cube Records is a TypeScript-first wrapper around Cube’s React hooks. It turns Cube’s useCubeQuery
hook into a type-safe somewhat opinionated API with an improved and IDE-friendly developer experience.
Before this, every Cube query felt like a shot in the dark. No types, no IntelliSense and limited confidence, especially because Cube model definitions were changing rapidly.
This fast-paced evolution came from helping an early-stage startup reach product-market fit. That kind of velocity is exciting, but it also demands better internal tools, which is exactly the kind of challenge I solve through my freelance services.
The Build Process
I worked with Claude to define the ideal usage pattern - what would it look like if Cube supported fully typed queries out of the box?
I initially implemented a lightweight solution that gave me some autocomplete while writing Cube queries, helping catch typos early. But the returned data wasn’t typed, which only solved half the problem. I started researching how to get fully typed results, first on my own, and increasingly with Claude helping as a research assistant, not yet as a coding partner. Then Claude Code came out, and it became a game changer. It helped me reason through the nuances of type generation and interface design in real time.
I wrote more about this here: TypeScript Global Interface Augmentation.
// With Cube Records - Full type safety for analytics queries
import { useCubeRecordQuery } from '@general-dexterity/cube-records';
// Monthly revenue by country with shipping metrics
const { data, isLoading } = useCubeRecordQuery({
model: 'orders', // ✅ TypeScript validates this exists
query: {
measures: ['count'], // ✅ Autocomplete
dimensions: ['shipping_country'], // ✅ Type-safe
timeDimensions: [{
dimension: 'order_date', // ✅ Only time fields allowed
granularity: 'month',
dateRange: 'last 12 months'
}]
}
});
console.log(data[0]);
// TypeScript knows the exact shape:
// {
// count: number,
// shipping_country: string,
// 'order_date.month': string
// }
The Result
- End-to-end type safety for queries, dimensions and measures
- Full IntelliSense and autocomplete support in VSCode
- Type-safe return values from Cube hooks
- More confident refactoring and safer production code
And as a bonus, it pushed to publish two npm packages:
cube-records
, wrapping Cube's react hooks with typescube-codegen
, a codegen tool creating the required types forcube-records
using Cube's meta endpoint.
Tool 2: Cube Explorer
What It Is
Cube Explorer is a Chrome extension that intercepts Cube server requests, parses them and displays them in a human-readable interface. It turns a noisy browser network tab into a clearer debugging dashboard.
Before, debugging Cube queries meant hunting through network logs, guessing which request matched which chart and cross-referencing IDs. Now? I open Cube Explorer and everything I need is right there.
The Build Process
Debugging Cube queries wasn’t something I did every day, but when it was needed, it was frustrating. Every couple of months I’d jot down ideas for a tool that would’ve made the process easier. At some point, I pasted those notes in a Claude conversation trying to figure out how to solve this.
When CRXJS v2.0 was released, it felt like I finally had everything needed to turn that long discussion into a real tool. Claude helped shape the architecture, from UI layout to request interception logic.
This time however, instead of reaching out to Claude to figure more abstract pieces of the code, I entirely delegated the implementation to Claude Code, only providing UX feedback while testing it on my different projects[1]. This was the "AI as driver, human as copilot" workflow in action.


The Result
- Instantly see all Cube queries and their responses
- Easily trace which request came from which component
- Reduce time spent debugging by minutes or even hours per session
Chrome Extension: Cube Explorer
Key Insight: Redefining What "Productive AI" Means
One thing stood out to me during this process: neither of these tools is customer-facing.
They don’t generate revenue. They don’t ship new product features.But they do increase the speed, safety and enjoyment of building those features.That’s the kind of productivity that compounds. When your dev environment feels better, you move faster and you make fewer mistakes.
The takeaway? Using AI to improve internal tools is just as powerful as using it to ship customer features.
AI makes this kind of tooling dramatically easier and faster to build.
Building internal tools for myself isn’t new. As a long-time neovim
user, I’ve written small plugins to streamline my workflow. And over the past five years, inspired by this article on home-cooked apps), I’ve been creating iOS tools just for fun, not profit.
That same mindset naturally extends to work: building quick, precise tools to be more productive or dig deeper when debugging. It’s another form of leverage - creative, technical and personal.
Results & Impact
Daily Use
Both Cube Records and Cube Explorer are used every day by myself and the team I currently work with. They’re now part of the standard development workflow.
Developer Experience Gains
- Fewer bugs caused by query typos
- Shorter feedback loops when debugging
- Better focus on business logic
- More confidence writing Cube-based features
- Even code agents can now write Cube queries more reliably. If they hallucinate a model, dimension or measure, the typechecker instantly flags it.
Before: Vanilla Cube.js
import { useCubeQuery } from '@cubejs-client/react';
// Monthly revenue analytics - no TypeScript validation
const { resultSet, isLoading } = useCubeQuery({
measures: ['orders.count', 'orders.totl_amount', 'orders.sum_shiping_cost'], // Typos!
dimensions: ['orders.shpping_country'], // Typos!
timeDimensions: [{
dimension: 'orders.order_date', // Manual prefixing everywhere
granularity: 'month',
dateRange: 'last 12 months'
}]
});
const data = resultSet?.tablePivot() || [];
console.log(data[0]);
// No types - runtime shape unknown:
// {
// 'orders.count': any, // Actually string "42"!
// 'orders.totl_amount': any, // undefined - typo!
// 'orders.sum_shiping_cost': any, // undefined - typo!
// 'orders.shipping_country': any, // string
// 'orders.order_date.month': any // string
// }
After: With Cube Records
import { useCubeRecordQuery } from '@general-dexterity/cube-records';
// Monthly revenue analytics with full type safety
const { data, isLoading } = useCubeRecordQuery({
model: 'orders',
query: {
measures: ['count', 'total_amount', 'sum_shipping_cost'], // ✅ Autocomplete
dimensions: ['shipping_country'], // ✅ Validated
timeDimensions: [{
dimension: 'order_date', // No prefixing
granularity: 'month',
dateRange: 'last 12 months'
}]
}
});
console.log(data[0]);
// Fully typed at compile-time:
// {
// count: number,
// total_amount: number,
// sum_shipping_cost: number,
// shipping_country: string,
// 'order_date.month': string
// }
The Lesson: Frustrations Are Opportunities[2]
Every time you think:
- “I wish this tool did X”
- “This part of my stack feels clunky”
…you’re identifying an opportunity for leverage.
Instead of:
- Filing a GitHub issue and waiting
- Building something from scratch over weeks
You can:
- Define the experience you want
- Use Claude or Claude Code to implement it
- Test, refine and deploy it in days, not months
- Do it without compromising on quality, even for internal tools
- Reduce long-term maintenance by iterating improvements via natural language, not refactoring alone
- And, hopefully, learn new things along the way[3]
Conclusion: The Developer's New Superpower
AI has changed what it means to be a productive developer.
It’s no longer just about what you can code, but about what you can design.
With the right mindset and a clear vision, you can:
- Identify friction in your stack
- Describe the ideal solution
- Let AI build the first version
- Ship better tools, faster
Cube Records and Cube Explorer came from simple questions and a willingness to experiment. That mindset shift from "builder" to "designer + AI collaborator" is the real superpower.
So next time something slows you down, don’t just tolerate it.
Design the fix. Let AI build it.
Footnotes
Thankfully, the stack used for the extension is similar, if not identical, to my day to day stack so I could pretty quickly spot and solve those rabbit holes AI tools tend to fall into. ↩︎
This isn't a novel idea at all (see Paul Graham's "How to get startup ideas") but maybe a simpler and/or more actionable way to act upon those frustrations. ↩︎
Like how gnarly those TypeScript declaration can get. ↩︎