drizzle-cube 0.1.13 → 0.1.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -9,7 +9,8 @@ Transform your Drizzle schema into a powerful, type-safe analytics platform with
9
9
 
10
10
  [![NPM Version](https://img.shields.io/npm/v/drizzle-cube)](https://www.npmjs.com/package/drizzle-cube)
11
11
  [![TypeScript](https://img.shields.io/badge/TypeScript-5.0+-blue)](https://www.typescriptlang.org/)
12
- [![Drizzle ORM](https://img.shields.io/badge/Drizzle%20ORM-0.33+-green)](https://orm.drizzle.team/)
12
+ [![Drizzle ORM](https://img.shields.io/badge/Drizzle%20ORM-0.44.4+-green)](https://orm.drizzle.team/)
13
+ [![Tailwind CSS](https://img.shields.io/badge/Tailwind%20CSS-3.4+-06B6D4)](https://tailwindcss.com/)
13
14
  [![MIT License](https://img.shields.io/badge/License-MIT-green.svg)](https://choosealicense.com/licenses/mit/)
14
15
 
15
16
  ## Why Drizzle Cube?
@@ -39,15 +40,14 @@ export const employees = pgTable('employees', {
39
40
  name: text('name').notNull(),
40
41
  email: text('email'),
41
42
  active: boolean('active').default(true),
42
- departmentId: integer('department_id'),
43
- organisationId: integer('organisation_id').notNull(),
44
- salary: integer('salary')
43
+ department: integer('department'),
44
+ organisation: integer('organisation').notNull()
45
45
  })
46
46
 
47
47
  export const departments = pgTable('departments', {
48
48
  id: integer('id').primaryKey(),
49
49
  name: text('name').notNull(),
50
- organisationId: integer('organisation_id').notNull()
50
+ organisation: integer('organisation').notNull()
51
51
  })
52
52
 
53
53
  export const schema = { employees, departments }
@@ -62,39 +62,23 @@ import { eq } from 'drizzle-orm'
62
62
  import { schema } from './schema'
63
63
 
64
64
  export const employeesCube = defineCube('Employees', {
65
- title: 'Employee Analytics',
66
-
67
- // Use Drizzle query structure for type safety
68
65
  sql: (ctx) => ({
69
66
  from: schema.employees,
70
- joins: [
71
- {
72
- table: schema.departments,
73
- on: eq(schema.employees.departmentId, schema.departments.id),
74
- type: 'left'
75
- }
76
- ],
77
- where: eq(schema.employees.organisationId, ctx.securityContext.organisationId)
67
+ where: eq(schema.employees.organisation, ctx.securityContext.organisationId)
78
68
  }),
79
69
 
80
70
  dimensions: {
81
71
  name: {
82
72
  name: 'name',
83
73
  title: 'Employee Name',
84
- sql: schema.employees.name,
85
- type: 'string'
74
+ type: 'string',
75
+ sql: schema.employees.name
86
76
  },
87
77
  email: {
88
- name: 'email',
89
- title: 'Email Address',
90
- sql: schema.employees.email,
91
- type: 'string'
92
- },
93
- departmentName: {
94
- name: 'departmentName',
95
- title: 'Department',
96
- sql: schema.departments.name,
97
- type: 'string'
78
+ name: 'email',
79
+ title: 'Email',
80
+ type: 'string',
81
+ sql: schema.employees.email
98
82
  }
99
83
  },
100
84
 
@@ -102,22 +86,8 @@ export const employeesCube = defineCube('Employees', {
102
86
  count: {
103
87
  name: 'count',
104
88
  title: 'Total Employees',
105
- sql: schema.employees.id,
106
- type: 'count'
107
- },
108
- totalSalary: {
109
- name: 'totalSalary',
110
- title: 'Total Salary',
111
- sql: schema.employees.salary,
112
- type: 'sum',
113
- format: 'currency'
114
- },
115
- avgSalary: {
116
- name: 'avgSalary',
117
- title: 'Average Salary',
118
- sql: schema.employees.salary,
119
- type: 'avg',
120
- format: 'currency'
89
+ type: 'count',
90
+ sql: schema.employees.id
121
91
  }
122
92
  }
123
93
  })
@@ -126,33 +96,22 @@ export const employeesCube = defineCube('Employees', {
126
96
  ### 4. Setup API Server
127
97
 
128
98
  ```typescript
129
- // server.ts
99
+ // server.ts (Hono example)
100
+ import { Hono } from 'hono'
130
101
  import { drizzle } from 'drizzle-orm/postgres-js'
131
102
  import { createCubeApp } from 'drizzle-cube/adapters/hono'
132
- import { SemanticLayerCompiler } from 'drizzle-cube/server'
133
103
  import postgres from 'postgres'
134
104
  import { schema } from './schema'
135
105
  import { employeesCube } from './cubes'
136
106
 
137
- // Setup Drizzle
138
- const client = postgres(process.env.DATABASE_URL!)
139
- const db = drizzle(client, { schema })
107
+ const db = drizzle(postgres(process.env.DATABASE_URL!), { schema })
140
108
 
141
- // Create semantic layer
142
- const semanticLayer = new SemanticLayerCompiler({
143
- drizzle: db,
144
- schema,
145
- engineType: 'postgres'
146
- })
147
- semanticLayer.registerCube(employeesCube)
148
-
149
- // Create API server with Cube.js compatibility
150
109
  const app = createCubeApp({
151
- semanticLayer,
110
+ cubes: [employeesCube],
152
111
  drizzle: db,
153
112
  schema,
154
- getSecurityContext: async (c) => ({
155
- organisationId: c.get('user')?.organisationId
113
+ getSecurityContext: async () => ({
114
+ organisationId: 1 // Your auth logic here
156
115
  })
157
116
  })
158
117
 
@@ -162,176 +121,55 @@ export default app
162
121
  ### 5. Query from Frontend
163
122
 
164
123
  ```typescript
165
- // Use with Cube.js React SDK
166
- import { useCubeQuery } from '@cubejs-client/react'
167
-
168
- function EmployeeStats() {
169
- const { resultSet, isLoading } = useCubeQuery({
170
- measures: ['Employees.count', 'Employees.avgSalary'],
171
- dimensions: ['Employees.departmentName'],
172
- filters: [
173
- { member: 'Employees.active', operator: 'equals', values: [true] }
174
- ]
175
- })
176
-
177
- if (isLoading) return <div>Loading...</div>
124
+ // Use built-in React components
125
+ import { QueryBuilder, AnalyticsDashboard } from 'drizzle-cube/client'
178
126
 
127
+ function App() {
179
128
  return (
180
- <table>
181
- {resultSet.tablePivot().map((row, i) => (
182
- <tr key={i}>
183
- <td>{row['Employees.departmentName']}</td>
184
- <td>{row['Employees.count']}</td>
185
- <td>${row['Employees.avgSalary']}</td>
186
- </tr>
187
- ))}
188
- </table>
129
+ <div>
130
+ <QueryBuilder apiUrl="/cubejs-api/v1" />
131
+ <AnalyticsDashboard
132
+ config={dashboardConfig}
133
+ baseUrl="/cubejs-api/v1"
134
+ />
135
+ </div>
189
136
  )
190
137
  }
191
138
  ```
192
139
 
193
140
  ## Key Features
194
141
 
195
- ### 🔐 Security First
196
-
197
- All SQL is generated using Drizzle's parameterized queries, making SQL injection impossible:
198
-
199
- ```typescript
200
- // ❌ Vulnerable (string concatenation)
201
- const sql = `WHERE name = '${userInput}'`
202
-
203
- // ✅ Safe (Drizzle parameterization)
204
- const condition = eq(schema.employees.name, userInput)
205
- ```
206
-
207
- ### 🏗️ Type Safety
208
-
209
- Get full TypeScript support from your database schema to your analytics:
210
-
211
- ```typescript
212
- const cube = defineCube('Employees', {
213
- dimensions: {
214
- name: {
215
- name: 'name',
216
- title: 'Employee Name',
217
- sql: schema.employees.name, // ✅ Type-safe
218
- type: 'string'
219
- },
220
- invalid: {
221
- name: 'invalid',
222
- sql: schema.employees.invalidCol, // ❌ TypeScript error
223
- type: 'string'
224
- }
225
- }
226
- })
227
- ```
228
-
229
- ### ⚡ Performance
230
-
231
- - **Prepared Statements**: Drizzle generates optimized prepared statements
232
- - **Query Planning**: Database optimizes repeated queries automatically
233
- - **Connection Pooling**: Leverages Drizzle's connection management
234
-
235
- ### 🧩 Framework Support
236
-
237
- Works with multiple frameworks via adapter pattern:
238
-
239
- - **Hono** - Built-in adapter
240
- - **Express** - Coming soon
241
- - **Fastify** - Coming soon
242
- - **Next.js** - Coming soon
243
-
244
- ## Advanced Usage
245
-
246
-
247
- ### Advanced Security with Row-Level Security
248
-
249
- ```typescript
250
- import { and, sql } from 'drizzle-orm'
251
-
252
- const secureCube = defineCube('SecureEmployees', {
253
- title: 'Secure Employee Data',
254
-
255
- sql: (ctx) => ({
256
- from: schema.employees,
257
- where: and(
258
- eq(schema.employees.organisationId, ctx.securityContext.organisationId),
259
- // Only show employees user has permission to see
260
- ctx.securityContext.role === 'admin'
261
- ? sql`true`
262
- : eq(schema.employees.managerId, ctx.securityContext.userId)
263
- )
264
- }),
265
-
266
- dimensions: {
267
- name: {
268
- name: 'name',
269
- title: 'Employee Name',
270
- sql: schema.employees.name,
271
- type: 'string'
272
- }
273
- },
274
-
275
- measures: {
276
- count: {
277
- name: 'count',
278
- title: 'Employee Count',
279
- sql: schema.employees.id,
280
- type: 'count'
281
- }
282
- }
283
- })
284
- ```
285
-
286
-
287
- ## API Reference
288
-
289
- ### Core Functions
290
-
291
- - `defineCube(name, definition)` - Create type-safe cube with name and configuration
292
- - `SemanticLayerCompiler({ drizzle, schema, engineType })` - Setup semantic layer compiler
293
- - `createCubeApp(options)` - Create Cube.js-compatible API server
294
-
295
- ### Supported Drizzle Features
296
-
297
- - ✅ **All Database Types** - PostgreSQL, MySQL, SQLite
298
- - ✅ **Query Builder** - Full Drizzle query builder support
299
- - ✅ **Schema References** - Direct column references
300
- - ✅ **SQL Templates** - Raw SQL with parameterization
301
- - ✅ **Aggregations** - count, sum, avg, min, max, countDistinct
302
- - ✅ **Joins** - Inner, left, right, full outer joins
303
- - ✅ **CTEs** - Common table expressions
304
- - ✅ **Subqueries** - Nested query support
305
- - ✅ **Window Functions** - Advanced analytics
306
- - ✅ **JSON Operations** - PostgreSQL JSON/JSONB support
142
+ 🔒 **SQL Injection Proof** - All queries use Drizzle's parameterized SQL
143
+ 🛡️ **Type Safe** - Full TypeScript inference from your database schema
144
+ **Performance** - Prepared statements and query optimization
145
+ 🧩 **Cube.js Compatible** - Works with existing Cube.js React components
146
+ 🎯 **Zero Config** - Infer cube definitions from your Drizzle schema
307
147
 
308
- ### Filter Operators
309
148
 
310
- Supports all Cube.js filter operators with Drizzle safety:
149
+ ## Supported Features
311
150
 
312
- - `equals`, `notEquals` `eq()`, `ne()`
313
- - `contains`, `notContains` `ilike()`, `notIlike()`
314
- - `gt`, `gte`, `lt`, `lte` `gt()`, `gte()`, `lt()`, `lte()`
315
- - `set`, `notSet` `isNotNull()`, `isNull()`
316
- - `inDateRange` `and(gte(), lte())`
151
+ **Multiple Database Types** - PostgreSQL, MySQL
152
+ ✅ **Framework Adapters** - Hono, Express, Fastify, Next.js
153
+ **Full Type Safety** - Complete TypeScript inference
154
+ ✅ **All SQL Features** - Joins, CTEs, subqueries, window functions
155
+ ✅ **Cube.js Compatibility** - Drop-in replacement for existing apps
317
156
 
318
157
  ## Documentation
319
158
 
320
- 📚 **[Full Documentation](https://www.drizzle-cube.dev/)** - Complete guides and API reference
321
- 🚀 **[Try the Sandbox](https://try.drizzle-cube.dev/)** - Working example version to experiment with
159
+ - 📚 **[Full Documentation](https://www.drizzle-cube.dev/)** - Complete guides and API reference
160
+ - 🚀 **[Try the Sandbox](https://try.drizzle-cube.dev/)** - Working example version to experiment with
322
161
 
323
162
  ### Local Development
324
163
  ```bash
325
- # Run documentation site locally
326
- npm run dev:help
327
-
328
- # Build documentation site
329
- npm run build:help
164
+ npm run dev
330
165
  ```
331
166
 
332
167
  ## Examples
333
168
 
334
- - **[Hono Example](https://github.com/cliftonc/drizzle-cube/tree/main/examples/hono)** - Full-featured dashboard with Cloudflare Workers
169
+ - **[Hono Example](https://github.com/cliftonc/drizzle-cube/tree/main/examples/hono)** - Full-featured dashboard with Cloudflare Workers support
170
+ - **[Express Example](https://github.com/cliftonc/drizzle-cube/tree/main/examples/express)** - Simple Express.js server with React dashboard
171
+ - **[Fastify Example](https://github.com/cliftonc/drizzle-cube/tree/main/examples/fastify)** - High-performance Fastify server with React client
172
+ - **[Next.js Example](https://github.com/cliftonc/drizzle-cube/tree/main/examples/nextjs)** - Full-stack Next.js app with API routes
335
173
 
336
174
  ## Contributing
337
175
 
@@ -339,9 +177,6 @@ We welcome contributions! Please see our [Contributing Guide](./CONTRIBUTING.md)
339
177
 
340
178
  ## Roadmap
341
179
 
342
- - 🔄 **Express Adapter** - Express.js integration
343
- - 🔄 **Fastify Adapter** - Fastify integration
344
- - 🔄 **Next.js Adapter** - Next.js API routes
345
180
  - 🔄 **Pre-aggregations** - Materialized view support
346
181
  - 🔄 **Real-time Updates** - WebSocket support
347
182
  - 🔄 **Query Caching** - Redis integration
@@ -1,2 +1,2 @@
1
1
  import { AnalyticsDashboardProps } from '../types';
2
- export default function AnalyticsDashboard({ config, apiUrl, apiOptions, editable, onConfigChange, onSave, onDirtyStateChange }: AnalyticsDashboardProps): import("react/jsx-runtime").JSX.Element;
2
+ export default function AnalyticsDashboard({ config, editable, onConfigChange, onSave, onDirtyStateChange }: AnalyticsDashboardProps): import("react/jsx-runtime").JSX.Element;
@@ -5,7 +5,6 @@ interface DashboardGridProps {
5
5
  onConfigChange?: (config: DashboardConfig) => void;
6
6
  onPortletRefresh?: (portletId: string) => void;
7
7
  onSave?: (config: DashboardConfig) => Promise<void> | void;
8
- apiUrl?: string;
9
8
  }
10
- export default function DashboardGrid({ config, editable, onConfigChange, onPortletRefresh, onSave, apiUrl }: DashboardGridProps): import("react/jsx-runtime").JSX.Element;
9
+ export default function DashboardGrid({ config, editable, onConfigChange, onPortletRefresh, onSave }: DashboardGridProps): import("react/jsx-runtime").JSX.Element;
11
10
  export {};
@@ -6,7 +6,6 @@ interface PortletEditModalProps {
6
6
  portlet?: PortletConfig | null;
7
7
  title: string;
8
8
  submitText: string;
9
- apiUrl?: string;
10
9
  }
11
- export default function PortletEditModal({ isOpen, onClose, onSave, portlet, title, submitText, apiUrl }: PortletEditModalProps): import("react/jsx-runtime").JSX.Element;
10
+ export default function PortletEditModal({ isOpen, onClose, onSave, portlet, title, submitText }: PortletEditModalProps): import("react/jsx-runtime").JSX.Element;
12
11
  export {};
@@ -59,7 +59,6 @@ export interface ApiConfig {
59
59
  apiToken: string;
60
60
  }
61
61
  export interface QueryBuilderProps {
62
- baseUrl: string;
63
62
  className?: string;
64
63
  initialQuery?: CubeQuery;
65
64
  disableLocalStorage?: boolean;
@@ -7,8 +7,8 @@ export { default as PortletEditModal } from './components/PortletEditModal';
7
7
  export { default as DashboardEditModal } from './components/DashboardEditModal';
8
8
  export { default as Modal } from './components/Modal';
9
9
  export { default as QueryBuilder } from './components/QueryBuilder';
10
- export { CubeProvider } from './providers/CubeProvider';
10
+ export { CubeProvider, useCubeContext } from './providers/CubeProvider';
11
11
  export { useCubeQuery } from './hooks/useCubeQuery';
12
12
  export { createCubeClient } from './client/CubeClient';
13
- export type { PortletConfig, ChartType, ChartAxisConfig, ChartDisplayConfig, CubeQuery, CubeQueryOptions, DashboardConfig } from './types';
13
+ export type { PortletConfig, ChartType, ChartAxisConfig, ChartDisplayConfig, CubeQuery, CubeQueryOptions, CubeApiOptions, DashboardConfig } from './types';
14
14
  export { createDashboardLayout, formatChartData } from './utils';