drizzle-cube 0.1.1 → 0.1.2
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 +136 -51
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -54,29 +54,68 @@ export const schema = { employees, departments }
|
|
|
54
54
|
|
|
55
55
|
```typescript
|
|
56
56
|
// cubes.ts
|
|
57
|
-
import { defineCube
|
|
57
|
+
import { defineCube } from 'drizzle-cube/server'
|
|
58
|
+
import { eq } from 'drizzle-orm'
|
|
58
59
|
import { schema } from './schema'
|
|
59
60
|
|
|
60
|
-
export const employeesCube = defineCube(
|
|
61
|
-
|
|
61
|
+
export const employeesCube = defineCube('Employees', {
|
|
62
|
+
title: 'Employee Analytics',
|
|
62
63
|
|
|
63
|
-
// Use Drizzle query
|
|
64
|
-
sql: (
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
64
|
+
// Use Drizzle query structure for type safety
|
|
65
|
+
sql: (ctx) => ({
|
|
66
|
+
from: schema.employees,
|
|
67
|
+
joins: [
|
|
68
|
+
{
|
|
69
|
+
table: schema.departments,
|
|
70
|
+
on: eq(schema.employees.departmentId, schema.departments.id),
|
|
71
|
+
type: 'left'
|
|
72
|
+
}
|
|
73
|
+
],
|
|
74
|
+
where: eq(schema.employees.organisationId, ctx.securityContext.organisationId)
|
|
75
|
+
}),
|
|
69
76
|
|
|
70
77
|
dimensions: {
|
|
71
|
-
name: {
|
|
72
|
-
|
|
73
|
-
|
|
78
|
+
name: {
|
|
79
|
+
name: 'name',
|
|
80
|
+
title: 'Employee Name',
|
|
81
|
+
sql: schema.employees.name,
|
|
82
|
+
type: 'string'
|
|
83
|
+
},
|
|
84
|
+
email: {
|
|
85
|
+
name: 'email',
|
|
86
|
+
title: 'Email Address',
|
|
87
|
+
sql: schema.employees.email,
|
|
88
|
+
type: 'string'
|
|
89
|
+
},
|
|
90
|
+
departmentName: {
|
|
91
|
+
name: 'departmentName',
|
|
92
|
+
title: 'Department',
|
|
93
|
+
sql: schema.departments.name,
|
|
94
|
+
type: 'string'
|
|
95
|
+
}
|
|
74
96
|
},
|
|
75
97
|
|
|
76
98
|
measures: {
|
|
77
|
-
count: {
|
|
78
|
-
|
|
79
|
-
|
|
99
|
+
count: {
|
|
100
|
+
name: 'count',
|
|
101
|
+
title: 'Total Employees',
|
|
102
|
+
sql: schema.employees.id,
|
|
103
|
+
type: 'count'
|
|
104
|
+
},
|
|
105
|
+
totalSalary: {
|
|
106
|
+
name: 'totalSalary',
|
|
107
|
+
title: 'Total Salary',
|
|
108
|
+
sql: schema.employees.salary,
|
|
109
|
+
type: 'sum',
|
|
110
|
+
format: 'currency'
|
|
111
|
+
},
|
|
112
|
+
avgSalary: {
|
|
113
|
+
name: 'avgSalary',
|
|
114
|
+
title: 'Average Salary',
|
|
115
|
+
sql: schema.employees.salary,
|
|
116
|
+
type: 'avg',
|
|
117
|
+
format: 'currency'
|
|
118
|
+
}
|
|
80
119
|
}
|
|
81
120
|
})
|
|
82
121
|
```
|
|
@@ -89,13 +128,19 @@ import { drizzle } from 'drizzle-orm/postgres-js'
|
|
|
89
128
|
import { createCubeApp } from 'drizzle-cube/adapters/hono'
|
|
90
129
|
import { SemanticLayerCompiler } from 'drizzle-cube/server'
|
|
91
130
|
import postgres from 'postgres'
|
|
131
|
+
import { schema } from './schema'
|
|
132
|
+
import { employeesCube } from './cubes'
|
|
92
133
|
|
|
93
134
|
// Setup Drizzle
|
|
94
135
|
const client = postgres(process.env.DATABASE_URL!)
|
|
95
136
|
const db = drizzle(client, { schema })
|
|
96
137
|
|
|
97
138
|
// Create semantic layer
|
|
98
|
-
const semanticLayer = new SemanticLayerCompiler({
|
|
139
|
+
const semanticLayer = new SemanticLayerCompiler({
|
|
140
|
+
drizzle: db,
|
|
141
|
+
schema,
|
|
142
|
+
engineType: 'postgres'
|
|
143
|
+
})
|
|
99
144
|
semanticLayer.registerCube(employeesCube)
|
|
100
145
|
|
|
101
146
|
// Create API server with Cube.js compatibility
|
|
@@ -161,10 +206,19 @@ const condition = eq(schema.employees.name, userInput)
|
|
|
161
206
|
Get full TypeScript support from your database schema to your analytics:
|
|
162
207
|
|
|
163
208
|
```typescript
|
|
164
|
-
const cube = defineCube(
|
|
209
|
+
const cube = defineCube('Employees', {
|
|
165
210
|
dimensions: {
|
|
166
|
-
name: {
|
|
167
|
-
|
|
211
|
+
name: {
|
|
212
|
+
name: 'name',
|
|
213
|
+
title: 'Employee Name',
|
|
214
|
+
sql: schema.employees.name, // ✅ Type-safe
|
|
215
|
+
type: 'string'
|
|
216
|
+
},
|
|
217
|
+
invalid: {
|
|
218
|
+
name: 'invalid',
|
|
219
|
+
sql: schema.employees.invalidCol, // ❌ TypeScript error
|
|
220
|
+
type: 'string'
|
|
221
|
+
}
|
|
168
222
|
}
|
|
169
223
|
})
|
|
170
224
|
```
|
|
@@ -189,10 +243,13 @@ Works with multiple frameworks via adapter pattern:
|
|
|
189
243
|
### Complex Queries with CTEs
|
|
190
244
|
|
|
191
245
|
```typescript
|
|
192
|
-
|
|
193
|
-
|
|
246
|
+
import { sql } from 'drizzle-orm'
|
|
247
|
+
|
|
248
|
+
const advancedCube = defineCube('DepartmentAnalytics', {
|
|
249
|
+
title: 'Department Analytics',
|
|
194
250
|
|
|
195
|
-
|
|
251
|
+
// For complex queries, you can use raw SQL
|
|
252
|
+
sql: (ctx) => sql`
|
|
196
253
|
WITH department_stats AS (
|
|
197
254
|
SELECT
|
|
198
255
|
d.id,
|
|
@@ -201,19 +258,35 @@ const advancedCube = defineCube(schema, {
|
|
|
201
258
|
AVG(e.salary) as avg_salary
|
|
202
259
|
FROM ${schema.departments} d
|
|
203
260
|
LEFT JOIN ${schema.employees} e ON d.id = e.department_id
|
|
204
|
-
WHERE d.organisation_id = ${securityContext.organisationId}
|
|
261
|
+
WHERE d.organisation_id = ${ctx.securityContext.organisationId}
|
|
205
262
|
GROUP BY d.id, d.name
|
|
206
263
|
)
|
|
207
264
|
SELECT * FROM department_stats
|
|
208
265
|
`,
|
|
209
266
|
|
|
210
267
|
dimensions: {
|
|
211
|
-
name: {
|
|
268
|
+
name: {
|
|
269
|
+
name: 'name',
|
|
270
|
+
title: 'Department Name',
|
|
271
|
+
sql: sql`name`,
|
|
272
|
+
type: 'string'
|
|
273
|
+
}
|
|
212
274
|
},
|
|
213
275
|
|
|
214
276
|
measures: {
|
|
215
|
-
employeeCount: {
|
|
216
|
-
|
|
277
|
+
employeeCount: {
|
|
278
|
+
name: 'employeeCount',
|
|
279
|
+
title: 'Employee Count',
|
|
280
|
+
sql: sql`employee_count`,
|
|
281
|
+
type: 'number'
|
|
282
|
+
},
|
|
283
|
+
avgSalary: {
|
|
284
|
+
name: 'avgSalary',
|
|
285
|
+
title: 'Average Salary',
|
|
286
|
+
sql: sql`avg_salary`,
|
|
287
|
+
type: 'number',
|
|
288
|
+
format: 'currency'
|
|
289
|
+
}
|
|
217
290
|
}
|
|
218
291
|
})
|
|
219
292
|
```
|
|
@@ -221,21 +294,39 @@ const advancedCube = defineCube(schema, {
|
|
|
221
294
|
### Advanced Security with Row-Level Security
|
|
222
295
|
|
|
223
296
|
```typescript
|
|
224
|
-
|
|
225
|
-
|
|
297
|
+
import { and, sql } from 'drizzle-orm'
|
|
298
|
+
|
|
299
|
+
const secureCube = defineCube('SecureEmployees', {
|
|
300
|
+
title: 'Secure Employee Data',
|
|
226
301
|
|
|
227
|
-
sql: (
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
.
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
302
|
+
sql: (ctx) => ({
|
|
303
|
+
from: schema.employees,
|
|
304
|
+
where: and(
|
|
305
|
+
eq(schema.employees.organisationId, ctx.securityContext.organisationId),
|
|
306
|
+
// Only show employees user has permission to see
|
|
307
|
+
ctx.securityContext.role === 'admin'
|
|
308
|
+
? sql`true`
|
|
309
|
+
: eq(schema.employees.managerId, ctx.securityContext.userId)
|
|
310
|
+
)
|
|
311
|
+
}),
|
|
312
|
+
|
|
313
|
+
dimensions: {
|
|
314
|
+
name: {
|
|
315
|
+
name: 'name',
|
|
316
|
+
title: 'Employee Name',
|
|
317
|
+
sql: schema.employees.name,
|
|
318
|
+
type: 'string'
|
|
319
|
+
}
|
|
320
|
+
},
|
|
321
|
+
|
|
322
|
+
measures: {
|
|
323
|
+
count: {
|
|
324
|
+
name: 'count',
|
|
325
|
+
title: 'Employee Count',
|
|
326
|
+
sql: schema.employees.id,
|
|
327
|
+
type: 'count'
|
|
328
|
+
}
|
|
329
|
+
}
|
|
239
330
|
})
|
|
240
331
|
```
|
|
241
332
|
|
|
@@ -259,9 +350,9 @@ import Database from 'better-sqlite3'
|
|
|
259
350
|
|
|
260
351
|
### Core Functions
|
|
261
352
|
|
|
262
|
-
- `defineCube(
|
|
263
|
-
- `
|
|
264
|
-
- `createCubeApp(options)` - Create Cube.js API server
|
|
353
|
+
- `defineCube(name, definition)` - Create type-safe cube with name and configuration
|
|
354
|
+
- `SemanticLayerCompiler({ drizzle, schema, engineType })` - Setup semantic layer compiler
|
|
355
|
+
- `createCubeApp(options)` - Create Cube.js-compatible API server
|
|
265
356
|
|
|
266
357
|
### Supported Drizzle Features
|
|
267
358
|
|
|
@@ -288,17 +379,11 @@ Supports all Cube.js filter operators with Drizzle safety:
|
|
|
288
379
|
|
|
289
380
|
## Documentation
|
|
290
381
|
|
|
291
|
-
|
|
292
|
-
🏗️ **[API Reference](https://drizzle-cube.dev/api)**
|
|
293
|
-
🎯 **[Drizzle Integration Guide](./docs/drizzle-integration.md)**
|
|
294
|
-
🚀 **[Migration Guide](https://drizzle-cube.dev/migration)**
|
|
382
|
+
Coming soon! 📚
|
|
295
383
|
|
|
296
384
|
## Examples
|
|
297
385
|
|
|
298
|
-
|
|
299
|
-
- **[Advanced Security](./examples/hono-security/)**
|
|
300
|
-
- **[Multi-tenant SaaS](./examples/multi-tenant/)**
|
|
301
|
-
- **[Real-time Dashboard](./examples/dashboard/)**
|
|
386
|
+
Coming soon! Check the test files for usage patterns in the meantime.
|
|
302
387
|
|
|
303
388
|
## Contributing
|
|
304
389
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "drizzle-cube",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "Drizzle ORM-first semantic layer with Cube.js compatibility. Type-safe analytics and dashboards with SQL injection protection.",
|
|
5
5
|
"main": "./dist/server/index.js",
|
|
6
6
|
"types": "./dist/server/index.d.ts",
|