metal-orm 1.0.6 → 1.0.8
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 +122 -121
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -8,8 +8,8 @@
|
|
|
8
8
|
|
|
9
9
|
MetalORM is a TypeScript-first, AST-driven SQL toolkit:
|
|
10
10
|
|
|
11
|
-
- At the **base level**, it's a clear, deterministic query builder with schema definitions and relation-aware hydration
|
|
12
|
-
- At the **next level**, it becomes an **ORM runtime** with entities, lazy/batched relations, and a Unit of Work (`OrmContext`) that flushes graph changes in one `saveChanges()`.
|
|
11
|
+
- At the **base level**, it's a clear, deterministic query builder with schema definitions and relation-aware hydration.
|
|
12
|
+
- At the **next level**, it becomes an **ORM runtime** with entities, lazy/batched relations, and a Unit of Work (`OrmContext`) that flushes graph changes in one `saveChanges()`.
|
|
13
13
|
|
|
14
14
|
Use only the parts you need: query builder + hydration for read-heavy/reporting code, or the full ORM runtime for application/business logic.
|
|
15
15
|
|
|
@@ -17,18 +17,18 @@ Use only the parts you need: query builder + hydration for read-heavy/reporting
|
|
|
17
17
|
|
|
18
18
|
## Documentation
|
|
19
19
|
|
|
20
|
-
Full docs live in the `docs/` folder:
|
|
21
|
-
|
|
22
|
-
- [Introduction](docs/index.md)
|
|
23
|
-
- [Getting Started](docs/getting-started.md)
|
|
24
|
-
- [Schema Definition](docs/schema-definition.md)
|
|
25
|
-
- [Query Builder](docs/query-builder.md)
|
|
26
|
-
- [DML Operations](docs/dml-operations.md)
|
|
27
|
-
- [Hydration & Entities](docs/hydration.md)
|
|
28
|
-
- [Runtime & Unit of Work](docs/runtime.md)
|
|
29
|
-
- [Advanced Features](docs/advanced-features.md)
|
|
30
|
-
- [Multi-Dialect Support](docs/multi-dialect-support.md)
|
|
31
|
-
- [API Reference](docs/api-reference.md)
|
|
20
|
+
Full docs live in the `docs/` folder:
|
|
21
|
+
|
|
22
|
+
- [Introduction](https://github.com/celsowm/metal-orm/blob/main/docs/index.md)
|
|
23
|
+
- [Getting Started](https://github.com/celsowm/metal-orm/blob/main/docs/getting-started.md)
|
|
24
|
+
- [Schema Definition](https://github.com/celsowm/metal-orm/blob/main/docs/schema-definition.md)
|
|
25
|
+
- [Query Builder](https://github.com/celsowm/metal-orm/blob/main/docs/query-builder.md)
|
|
26
|
+
- [DML Operations](https://github.com/celsowm/metal-orm/blob/main/docs/dml-operations.md)
|
|
27
|
+
- [Hydration & Entities](https://github.com/celsowm/metal-orm/blob/main/docs/hydration.md)
|
|
28
|
+
- [Runtime & Unit of Work](https://github.com/celsowm/metal-orm/blob/main/docs/runtime.md)
|
|
29
|
+
- [Advanced Features](https://github.com/celsowm/metal-orm/blob/main/docs/advanced-features.md)
|
|
30
|
+
- [Multi-Dialect Support](https://github.com/celsowm/metal-orm/blob/main/docs/multi-dialect-support.md)
|
|
31
|
+
- [API Reference](https://github.com/celsowm/metal-orm/blob/main/docs/api-reference.md)
|
|
32
32
|
|
|
33
33
|
---
|
|
34
34
|
|
|
@@ -36,12 +36,12 @@ Full docs live in the `docs/` folder:
|
|
|
36
36
|
|
|
37
37
|
**As a query builder:**
|
|
38
38
|
|
|
39
|
-
- **Declarative schema definition** with `defineTable`, `col.*`, and typed relations
|
|
40
|
-
- **Fluent query builder** over a real SQL AST (`SelectQueryBuilder`, `InsertQueryBuilder`, `UpdateQueryBuilder`, `DeleteQueryBuilder`).
|
|
41
|
-
- **Advanced SQL**: CTEs, aggregates, window functions, subqueries, JSON, CASE, EXISTS.
|
|
42
|
-
- **Relation hydration**: turn flat rows into nested objects (`user.posts`, `user.roles`, etc.)
|
|
43
|
-
- **Multi-dialect**: compile once, run on MySQL, PostgreSQL, SQLite, or SQL Server
|
|
44
|
-
- **DML**: type-safe INSERT / UPDATE / DELETE with `RETURNING` where supported
|
|
39
|
+
- **Declarative schema definition** with `defineTable`, `col.*`, and typed relations.
|
|
40
|
+
- **Fluent query builder** over a real SQL AST (`SelectQueryBuilder`, `InsertQueryBuilder`, `UpdateQueryBuilder`, `DeleteQueryBuilder`).
|
|
41
|
+
- **Advanced SQL**: CTEs, aggregates, window functions, subqueries, JSON, CASE, EXISTS.
|
|
42
|
+
- **Relation hydration**: turn flat rows into nested objects (`user.posts`, `user.roles`, etc.).
|
|
43
|
+
- **Multi-dialect**: compile once, run on MySQL, PostgreSQL, SQLite, or SQL Server.
|
|
44
|
+
- **DML**: type-safe INSERT / UPDATE / DELETE with `RETURNING` where supported.
|
|
45
45
|
|
|
46
46
|
**As an ORM runtime (optional):**
|
|
47
47
|
|
|
@@ -62,27 +62,29 @@ npm install metal-orm
|
|
|
62
62
|
# yarn
|
|
63
63
|
yarn add metal-orm
|
|
64
64
|
|
|
65
|
-
# pnpm
|
|
66
|
-
pnpm add metal-orm
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
MetalORM compiles SQL; you bring your own driver:
|
|
70
|
-
|
|
71
|
-
Dialect
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
65
|
+
# pnpm
|
|
66
|
+
pnpm add metal-orm
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
MetalORM compiles SQL; you bring your own driver:
|
|
70
|
+
|
|
71
|
+
| Dialect | Driver | Install |
|
|
72
|
+
| --- | --- | --- |
|
|
73
|
+
| MySQL / MariaDB | `mysql2` | `npm install mysql2` |
|
|
74
|
+
| SQLite | `sqlite3` | `npm install sqlite3` |
|
|
75
|
+
| PostgreSQL | `pg` | `npm install pg` |
|
|
76
|
+
| SQL Server | `tedious` | `npm install tedious` |
|
|
77
|
+
|
|
78
|
+
Pick the matching dialect (MySqlDialect, SQLiteDialect, PostgresDialect, MSSQLDialect) when compiling queries.
|
|
79
|
+
|
|
80
|
+
## Quick start
|
|
81
|
+
|
|
82
|
+
1. Start simple: tiny table, tiny query
|
|
83
|
+
|
|
84
|
+
MetalORM can be just a straightforward query builder.
|
|
85
|
+
|
|
86
|
+
```ts
|
|
87
|
+
import mysql from 'mysql2/promise';
|
|
86
88
|
import {
|
|
87
89
|
defineTable,
|
|
88
90
|
col,
|
|
@@ -114,22 +116,24 @@ const { sql, params } = listOpenTodos.compile(dialect);
|
|
|
114
116
|
|
|
115
117
|
// 4) Run with your favorite driver
|
|
116
118
|
const connection = await mysql.createConnection({ /* ... */ });
|
|
117
|
-
const [rows] = await connection.execute(sql, params);
|
|
118
|
-
|
|
119
|
-
console.log(rows);
|
|
120
|
-
// [
|
|
121
|
-
// { id: 1, title: 'Write docs', done: 0 },
|
|
122
|
-
// { id: 2, title: 'Ship feature', done: 0 },
|
|
123
|
-
// ]
|
|
119
|
+
const [rows] = await connection.execute(sql, params);
|
|
120
|
+
|
|
121
|
+
console.log(rows);
|
|
122
|
+
// [
|
|
123
|
+
// { id: 1, title: 'Write docs', done: 0 },
|
|
124
|
+
// { id: 2, title: 'Ship feature', done: 0 },
|
|
125
|
+
// ]
|
|
126
|
+
```
|
|
124
127
|
|
|
125
128
|
|
|
126
129
|
That’s it: schema, query, SQL, done.
|
|
127
130
|
|
|
128
131
|
2. Relations & hydration: nested results without an ORM
|
|
129
132
|
|
|
130
|
-
Now let
|
|
131
|
-
|
|
132
|
-
|
|
133
|
+
Now let's add relations and get nested objects, still without committing to a full ORM.
|
|
134
|
+
|
|
135
|
+
```ts
|
|
136
|
+
import {
|
|
133
137
|
defineTable,
|
|
134
138
|
col,
|
|
135
139
|
hasMany,
|
|
@@ -172,41 +176,43 @@ const builder = new SelectQueryBuilder(users)
|
|
|
172
176
|
columns: [posts.columns.id, posts.columns.title, posts.columns.createdAt],
|
|
173
177
|
}); // eager relation for hydration
|
|
174
178
|
|
|
175
|
-
const { sql, params } = builder.compile(dialect);
|
|
176
|
-
const [rows] = await connection.execute(sql, params);
|
|
177
|
-
|
|
178
|
-
// Turn flat rows into nested objects
|
|
179
|
-
const hydrated = hydrateRows(
|
|
180
|
-
rows as Record<string, unknown>[],
|
|
181
|
-
builder.getHydrationPlan(),
|
|
182
|
-
);
|
|
183
|
-
|
|
184
|
-
console.log(hydrated);
|
|
185
|
-
// [
|
|
186
|
-
// {
|
|
187
|
-
// id: 1,
|
|
188
|
-
// name: 'John Doe',
|
|
189
|
-
// email: 'john@example.com',
|
|
190
|
-
// postCount: 15,
|
|
191
|
-
// rank: 1,
|
|
192
|
-
// posts: [
|
|
193
|
-
// { id: 101, title: 'Latest Post', createdAt: '2023-05-15T10:00:00Z' },
|
|
194
|
-
// // ...
|
|
195
|
-
// ],
|
|
196
|
-
// },
|
|
197
|
-
// // ...
|
|
198
|
-
// ]
|
|
179
|
+
const { sql, params } = builder.compile(dialect);
|
|
180
|
+
const [rows] = await connection.execute(sql, params);
|
|
181
|
+
|
|
182
|
+
// Turn flat rows into nested objects
|
|
183
|
+
const hydrated = hydrateRows(
|
|
184
|
+
rows as Record<string, unknown>[],
|
|
185
|
+
builder.getHydrationPlan(),
|
|
186
|
+
);
|
|
187
|
+
|
|
188
|
+
console.log(hydrated);
|
|
189
|
+
// [
|
|
190
|
+
// {
|
|
191
|
+
// id: 1,
|
|
192
|
+
// name: 'John Doe',
|
|
193
|
+
// email: 'john@example.com',
|
|
194
|
+
// postCount: 15,
|
|
195
|
+
// rank: 1,
|
|
196
|
+
// posts: [
|
|
197
|
+
// { id: 101, title: 'Latest Post', createdAt: '2023-05-15T10:00:00Z' },
|
|
198
|
+
// // ...
|
|
199
|
+
// ],
|
|
200
|
+
// },
|
|
201
|
+
// // ...
|
|
202
|
+
// ]
|
|
203
|
+
```
|
|
199
204
|
|
|
200
205
|
|
|
201
206
|
Use this mode anywhere you want powerful SQL + nice nested results, without changing how you manage your models.
|
|
202
207
|
|
|
203
208
|
3. Turn it up: entities + Unit of Work (ORM mode)
|
|
204
209
|
|
|
205
|
-
When you
|
|
206
|
-
|
|
207
|
-
Instead of
|
|
208
|
-
|
|
209
|
-
|
|
210
|
+
When you're ready, you can let MetalORM manage entities and relations for you.
|
|
211
|
+
|
|
212
|
+
Instead of "naked objects", your queries can return entities attached to an OrmContext:
|
|
213
|
+
|
|
214
|
+
```ts
|
|
215
|
+
import {
|
|
210
216
|
OrmContext,
|
|
211
217
|
MySqlDialect,
|
|
212
218
|
SelectQueryBuilder,
|
|
@@ -251,49 +257,44 @@ const newPost = user.posts.add({ title: 'Hello from ORM mode' });
|
|
|
251
257
|
// Many-to-many via pivot:
|
|
252
258
|
await user.roles.syncByIds([1, 2, 3]);
|
|
253
259
|
|
|
254
|
-
// 3) Persist the entire graph
|
|
255
|
-
await ctx.saveChanges();
|
|
256
|
-
// INSERT/UPDATE/DELETE + pivot updates happen in a single Unit of Work.
|
|
257
|
-
|
|
260
|
+
// 3) Persist the entire graph
|
|
261
|
+
await ctx.saveChanges();
|
|
262
|
+
// INSERT/UPDATE/DELETE + pivot updates happen in a single Unit of Work.
|
|
263
|
+
```
|
|
258
264
|
|
|
259
|
-
Here’s what the runtime gives you:
|
|
260
265
|
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
Relation tracking: add/remove/sync on relation collections emits relation changes.
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
Single flush: ctx.saveChanges() figures out inserts, updates, deletes, and pivot changes.
|
|
266
|
+
Here's what the runtime gives you:
|
|
267
|
+
|
|
268
|
+
- Identity map: the same row becomes the same entity instance within a context.
|
|
269
|
+
- Change tracking: field writes mark entities as dirty.
|
|
270
|
+
- Relation tracking: add/remove/sync on relation collections emits relation changes.
|
|
271
|
+
- Cascades: relation definitions can opt into cascade: 'all' | 'persist' | 'remove' | 'link'.
|
|
272
|
+
- Single flush: `ctx.saveChanges()` figures out inserts, updates, deletes, and pivot changes.
|
|
270
273
|
|
|
271
274
|
You can start your project using only the query builder + hydration and gradually migrate hot paths to entities as you implement the runtime primitives.
|
|
272
275
|
|
|
273
|
-
When to use which mode?
|
|
274
|
-
|
|
275
|
-
Query builder + hydration only
|
|
276
|
-
|
|
277
|
-
Great for reporting, analytics, and places where you already have a model layer.
|
|
278
|
-
|
|
279
|
-
You keep full control over how objects map to rows.
|
|
280
|
-
|
|
281
|
-
Entity + Unit of Work runtime
|
|
282
|
-
|
|
283
|
-
Great for request-scoped application logic and domain modeling.
|
|
284
|
-
|
|
285
|
-
You want lazy relations, cascades, and less boilerplate around update/delete logic.
|
|
286
|
-
|
|
287
|
-
Both modes share the same schema, AST, and dialects, so you don't have to pick one forever.
|
|
288
|
-
|
|
289
|
-
Contributing
|
|
290
|
-
|
|
291
|
-
Issues and PRs are welcome! If you're interested in pushing the runtime/ORM side further (soft deletes, multi-tenant filters, outbox patterns, etc.), contributions are especially appreciated.
|
|
292
|
-
|
|
293
|
-
See the
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
MetalORM is MIT licensed
|
|
299
|
-
.
|
|
276
|
+
## When to use which mode?
|
|
277
|
+
|
|
278
|
+
### Query builder + hydration only
|
|
279
|
+
|
|
280
|
+
Great for reporting, analytics, and places where you already have a model layer.
|
|
281
|
+
|
|
282
|
+
You keep full control over how objects map to rows.
|
|
283
|
+
|
|
284
|
+
### Entity + Unit of Work runtime
|
|
285
|
+
|
|
286
|
+
Great for request-scoped application logic and domain modeling.
|
|
287
|
+
|
|
288
|
+
You want lazy relations, cascades, and less boilerplate around update/delete logic.
|
|
289
|
+
|
|
290
|
+
Both modes share the same schema, AST, and dialects, so you don't have to pick one forever.
|
|
291
|
+
|
|
292
|
+
## Contributing
|
|
293
|
+
|
|
294
|
+
Issues and PRs are welcome! If you're interested in pushing the runtime/ORM side further (soft deletes, multi-tenant filters, outbox patterns, etc.), contributions are especially appreciated.
|
|
295
|
+
|
|
296
|
+
See the contributing guide for details.
|
|
297
|
+
|
|
298
|
+
## License
|
|
299
|
+
|
|
300
|
+
MetalORM is MIT licensed.
|