@uql/core 3.1.0 → 3.1.1
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/CHANGELOG.md +11 -0
- package/dist/CHANGELOG.md +186 -0
- package/dist/README.md +413 -0
- package/dist/package.json +2 -2
- package/package.json +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,17 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
+
## [3.1.1](https://github.com/rogerpadilla/uql/compare/@uql/core@3.1.0...@uql/core@3.1.1) (2025-12-30)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
### Bug Fixes
|
|
10
|
+
|
|
11
|
+
* adjust relative paths for README and CHANGELOG in copyfiles script ([741c2ee](https://github.com/rogerpadilla/uql/commit/741c2ee8839376ca89a860a53950ef6b6d234596))
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
|
|
6
17
|
# [3.1.0](https://github.com/rogerpadilla/uql/compare/@uql/core@3.0.0...@uql/core@3.1.0) (2025-12-30)
|
|
7
18
|
|
|
8
19
|
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file. Please add new changes to the top.
|
|
4
|
+
|
|
5
|
+
date format is [yyyy-mm-dd]
|
|
6
|
+
## [2.0.0] - 2025-12-29
|
|
7
|
+
- **Major Rebranding**: Rebranded the project from **Nukak** to **UQL** (Universal Query Language - this was the original name!).
|
|
8
|
+
- New Slogan: **"One Language. Frontend to Backend."**
|
|
9
|
+
- Project homepage: [uql.app](https://uql.app).
|
|
10
|
+
- **Package Unification**: Unified all database adapters (`mysql`, `postgres`, `maria`, `sqlite`, `mongo`) and `express` middleware into a single core package: `@uql/core`.
|
|
11
|
+
- **Scoped Naming**:
|
|
12
|
+
- `@uql/core`: The main ORM engine and all database adapters.
|
|
13
|
+
- `@uql/migrate`: The database migration system (formerly `nukak-migrate`).
|
|
14
|
+
- **Improved API Surface**:
|
|
15
|
+
- Database-specific logic is now accessible via sub-paths (e.g., `import { ... } from '@uql/core/postgres'`).
|
|
16
|
+
- Unified `NamingStrategy` and `QueryContext` across all unified adapters.
|
|
17
|
+
- **Build & Distribution**:
|
|
18
|
+
- Integrated `bunchee` for high-performance browser bundle generation (`@uql/core/browser`).
|
|
19
|
+
- Minimized core dependency footprint by moving database drivers to optional `peerDependencies`.
|
|
20
|
+
- **Enhanced Type Safety**: Fully updated internal type resolution to support the unified package structure.
|
|
21
|
+
|
|
22
|
+
## [1.8.0] - 2025-12-29
|
|
23
|
+
- **New Feature**: Added support for **Naming Strategies**.
|
|
24
|
+
- Automatically translate TypeScript entity and property names to database-specific identifiers (e.g., camelCase to snake_case).
|
|
25
|
+
- Built-in `DefaultNamingStrategy` and `SnakeCaseNamingStrategy`.
|
|
26
|
+
- Comprehensive support across all SQL dialects and MongoDB.
|
|
27
|
+
- **Refactoring**:
|
|
28
|
+
- Unified naming and metadata resolution logic into a new `AbstractDialect` base class shared by both DML (Dialects) and DDL (Schema Generators).
|
|
29
|
+
- Improved `MongoDialect` to respect naming strategies for collection and field names on both read and write operations.
|
|
30
|
+
|
|
31
|
+
## [1.7.0] - 2025-12-29
|
|
32
|
+
- **New Package**: Introduced `nukak-migrate` for database migrations.
|
|
33
|
+
- Supports version-controlled schema changes via local migration files.
|
|
34
|
+
- Automatic migration generation from entity definitions using schema introspection.
|
|
35
|
+
- Full support for PostgreSQL, MySQL, MariaDB, and SQLite.
|
|
36
|
+
- CLI tool for managing migrations (`up`, `down`, `status`, `generate`, `sync`).
|
|
37
|
+
- Database-backed migration tracking (Database or JSON storage).
|
|
38
|
+
- **Core Improvements**:
|
|
39
|
+
- Expanded `@Field()` decorator with schema metadata: `length`, `precision`, `scale`, `unique`, `index`, `columnType`, `defaultValue`, and `comment`.
|
|
40
|
+
- Added schema generation and introspection capabilities to SQL dialects.
|
|
41
|
+
|
|
42
|
+
## [1.6.0] - 2025-12-28
|
|
43
|
+
- **Architectural Change**: Migrated from "Values as Parameter" to "Context Object" pattern for SQL generation.
|
|
44
|
+
- This pattern centralizes query parameters and SQL fragments into a `QueryContext`, ensuring robust placeholder management and preventing out-of-sync parameter indices.
|
|
45
|
+
- Improved compatibility with PostgreSQL's indexed placeholders ($1, $2, etc.) and complex sub-queries.
|
|
46
|
+
- Standardized dialect interfaces to operate directly on the `QueryContext` for higher performance and cleaner code.
|
|
47
|
+
- Fixed linter issues and unified type safety for `raw()` SQL snippets across all drivers.
|
|
48
|
+
|
|
49
|
+
## [1.5.0] - 2025-12-28
|
|
50
|
+
- **BREAKING CHANGE**: Implemented "Sticky Connections" for performance. `Querier` instances now hold their connection until `release()` is explicitly called.
|
|
51
|
+
- If you manually retrieve a querier via `pool.getQuerier()`, you **MUST** call `await querier.release()` when finished, otherwise connections will leak.
|
|
52
|
+
- `Repositories` and `pool.transaction(...)` callbacks automatically handle this, so high-level usage remains unchanged.
|
|
53
|
+
- Unified serialization logic: `@Serialized()` decorator is now centralized in `AbstractSqlQuerier`, removing redundant overrides in drivers.
|
|
54
|
+
- Fixed MongoDB consistency: `beginTransaction`, `commitTransaction`, and `rollbackTransaction` are now serialized to prevent race conditions.
|
|
55
|
+
- Fix Cross-Dialect SQL JSON bug by moving PostgreSQL-specific casts to the appropriate dialect.
|
|
56
|
+
- Fix transaction race conditions by serializing transaction lifecycle methods and implementing an internal execution pattern.
|
|
57
|
+
|
|
58
|
+
## [1.4.16] - 2025-12-28
|
|
59
|
+
|
|
60
|
+
- Implement a "Serialized Task Queue" at the core of the framework to ensure database connections are thread-safe and race-condition free.
|
|
61
|
+
- Introduce `@Serialized()` decorator to simplify the serialization of database operations across all drivers.
|
|
62
|
+
|
|
63
|
+
## [1.4.14] - 2025-12-28
|
|
64
|
+
|
|
65
|
+
- Robust `upsert` implementation across all SQL dialects (PostgreSQL, MySQL, MariaDB, SQLite).
|
|
66
|
+
|
|
67
|
+
## [1.4.10] - 2025-12-27
|
|
68
|
+
|
|
69
|
+
- Improve types, tests, migrate from EsLint/Prettier to Biome, and update dependencies.
|
|
70
|
+
|
|
71
|
+
## [1.4.6] - 2024-11-06
|
|
72
|
+
|
|
73
|
+
- Update dependencies and improve readme.
|
|
74
|
+
|
|
75
|
+
## [1.4.5] - 2024-09-26
|
|
76
|
+
|
|
77
|
+
- Imperative transactions have to be closed manually.
|
|
78
|
+
|
|
79
|
+
## [1.4.4] - 2024-09-26
|
|
80
|
+
|
|
81
|
+
- Ensure own connection is always released even if exception occurs.
|
|
82
|
+
- Correct issue when empty or null list is passed to `insertMany` operations.
|
|
83
|
+
|
|
84
|
+
## [1.4.3] - 2024-09-25
|
|
85
|
+
|
|
86
|
+
- Ensure the connection is auto-released after `commit` or `rollback` runs.
|
|
87
|
+
- Update dependencies.
|
|
88
|
+
|
|
89
|
+
## [1.4.2] - 2024-09-20
|
|
90
|
+
|
|
91
|
+
- Fix projection of `@OneToMany` field when the 'one' side produces empty result.
|
|
92
|
+
- Update dependencies.
|
|
93
|
+
|
|
94
|
+
## [1.4.1] - 2024-08-21
|
|
95
|
+
|
|
96
|
+
- Add nukak-maku logo.
|
|
97
|
+
- Update dependencies (functionality keeps the same in this release).
|
|
98
|
+
|
|
99
|
+
## [1.4.0] - 2024-08-15
|
|
100
|
+
|
|
101
|
+
- Automatically release the querier unless it is inside a current transaction.
|
|
102
|
+
- Remove unnecessary wrapper for transactions from `AbstractQuerierPool` class.
|
|
103
|
+
|
|
104
|
+
## [1.3.3] - 2024-08-13
|
|
105
|
+
|
|
106
|
+
- Improve typings of first inserted ID.
|
|
107
|
+
|
|
108
|
+
## [1.3.2] - 2024-08-13
|
|
109
|
+
|
|
110
|
+
- Return the inserted IDs in the response of the queriers' `run` function.
|
|
111
|
+
|
|
112
|
+
## [1.3.1] - 2024-08-13
|
|
113
|
+
|
|
114
|
+
- Fix an issue related to the `$where` condition of selected relations missed in the final criteria for `@OneToMany` and `@ManyToMany` relationships.
|
|
115
|
+
|
|
116
|
+
## [1.3.0] - 2024-08-13
|
|
117
|
+
|
|
118
|
+
- Add support for `json` and `jsonb` fields. Automatically parse the JSON values when persisting with `JSON.parse` function.
|
|
119
|
+
- Improve type-safety in general.
|
|
120
|
+
- Move `getPersistables` inside dialect for higher reusability.
|
|
121
|
+
- Add support for `vector` fields.
|
|
122
|
+
|
|
123
|
+
## [1.2.0] - 2024-08-12
|
|
124
|
+
|
|
125
|
+
- Add support for `raw` in values (previously, it was only supported by `$select` and `$where` operators). Allows safe use of any SQL query/clause as the value in an insert or update operation that shouldn't be automatically escaped by the ORM.
|
|
126
|
+
|
|
127
|
+
## [1.1.0] - 2024-08-11
|
|
128
|
+
|
|
129
|
+
- Add support for `upsert` operations.
|
|
130
|
+
- Migrate SQLite package driver from `sqlite3` to `better-sqlite3` for better performance.
|
|
131
|
+
- Make Maria package to use the `RETURNING id` clause to get the inserted IDs.
|
|
132
|
+
|
|
133
|
+
## [1.0.1] - 2024-08-10
|
|
134
|
+
|
|
135
|
+
- Rename `$project` operator to `$select` for consistency with most established frameworks so far.
|
|
136
|
+
- Rename `$filter` operator to `$where` for consistency with most established frameworks so far.
|
|
137
|
+
|
|
138
|
+
## [1.0.0] - 2024-08-10
|
|
139
|
+
|
|
140
|
+
- Allow to set a field as non-eager (i.e. lazy) with `eager: false` (by default fields are `eager: true`).
|
|
141
|
+
- Allow to set a field as non-updatable (i.e. insertable and read-only) with `updatable: false` (by default fields are `updatable: true`).
|
|
142
|
+
|
|
143
|
+
## [0.4.0] - 2023-11-06
|
|
144
|
+
|
|
145
|
+
- Move project inside query parameter [#63](https://github.com/rogerpadilla/nukak/pull/63)
|
|
146
|
+
|
|
147
|
+
## [0.3.3] - 2023-10-25
|
|
148
|
+
|
|
149
|
+
- Update usage example in the README.md.
|
|
150
|
+
|
|
151
|
+
## [0.3.2] - 2023-10-24
|
|
152
|
+
|
|
153
|
+
- Improve usage examples in the README.md, and make the overview section more concise.
|
|
154
|
+
|
|
155
|
+
## [0.3.1] - 2023-10-19
|
|
156
|
+
|
|
157
|
+
1. Remove `$group` and `$having` as they detriment type safety as currently implemented (support may be redesigned later if required).
|
|
158
|
+
2. Improve type safety of `$project` operator.
|
|
159
|
+
3. Improve type safety of `$filter` operator.
|
|
160
|
+
4. Remove projection operators (`$count`, `$min`, `$max`, `$min`, and `$sum`) as they detriment type safety as currently implemented. This can be done via Virtual fields instead as currently supported for better type safety.
|
|
161
|
+
|
|
162
|
+
## [0.3.0] - 2023-10-18
|
|
163
|
+
|
|
164
|
+
- Add support for `transaction` operations using a QuerierPool.
|
|
165
|
+
Automatically wraps the code of the callback inside a transaction, and auto-releases the querier after running.
|
|
166
|
+
- Update dependencies.
|
|
167
|
+
|
|
168
|
+
```ts
|
|
169
|
+
const ids = await querierPool.transaction(async (querier) => {
|
|
170
|
+
const data = await querier.findMany(...);
|
|
171
|
+
const ids = await querier.insertMany(...);
|
|
172
|
+
return ids;
|
|
173
|
+
});
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
## [0.2.21] 2023-04-15
|
|
177
|
+
|
|
178
|
+
- fix(nukak-browser): check if ids are returned before use $in to delete them.
|
|
179
|
+
|
|
180
|
+
- Reuse community open-source npm packages to escape literal-values according to each DB vendor.
|
|
181
|
+
|
|
182
|
+
## [0.2.0] 2023-01-02
|
|
183
|
+
|
|
184
|
+
- Move projection to a new parameter to improve type inference of the results.
|
|
185
|
+
|
|
186
|
+
- Support dynamic operations while projecting fields, and move `$project` as an independent parameter in the `find*` functions [#55](https://github.com/rogerpadilla/nukak/pull/55).
|
package/dist/README.md
ADDED
|
@@ -0,0 +1,413 @@
|
|
|
1
|
+
<!--  -->
|
|
2
|
+
|
|
3
|
+
[](https://uql.app)
|
|
4
|
+
|
|
5
|
+
[](https://github.com/rogerpadilla/uql) [](https://coveralls.io/r/rogerpadilla/uql?branch=main) [](https://github.com/rogerpadilla/uql/blob/main/LICENSE) [](https://www.npmjs.com/package/@uql/core)
|
|
6
|
+
|
|
7
|
+
[uql](https://uql.app) is the [smartest ORM](https://medium.com/@rogerpadillac/in-search-of-the-perfect-orm-e01fcc9bce3d) for TypeScript, it is designed to be fast, safe, and easy to integrate into any application.
|
|
8
|
+
|
|
9
|
+
It can run in Node.js, Browser, React Native, Expo, Electron, Deno, Bun, and many more!
|
|
10
|
+
|
|
11
|
+
Uses a consistent API for distinct databases, including PostgreSQL, MySQL, MariaDB, and SQLite (inspired by MongoDB glorious syntax).
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
```ts
|
|
16
|
+
const companyUsers = await userRepository.findMany({
|
|
17
|
+
$select: { email: true, profile: { $select: { picture: true } } },
|
|
18
|
+
$where: { email: { $endsWith: '@domain.com' } },
|
|
19
|
+
$sort: { createdAt: 'desc' },
|
|
20
|
+
$limit: 100,
|
|
21
|
+
});
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
## Why uql?
|
|
27
|
+
|
|
28
|
+
See [this article](https://medium.com/@rogerpadillac/in-search-of-the-perfect-orm-e01fcc9bce3d) in medium.com.
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
## Features
|
|
33
|
+
|
|
34
|
+
- **Type-safe and Context-aware queries**: Squeeze the power of `TypeScript` for auto-completion and validation of operators at any depth, [including relations and their fields](https://www.uql.app/docs/querying-relations).
|
|
35
|
+
- **Context-Object SQL Generation**: Uses a sophisticated `QueryContext` pattern to ensure perfectly indexed placeholders ($1, $2, etc.) and robust SQL fragment management, even in the most complex sub-queries.
|
|
36
|
+
- **Unified API across Databases**: Write once, run anywhere. Seamlessly switch between `PostgreSQL`, `MySQL`, `MariaDB`, `SQLite`, and even `MongoDB`.
|
|
37
|
+
- **Serializable JSON Syntax**: Queries can be expressed as `100%` valid `JSON`, allowing them to be easily transported from frontend to backend.
|
|
38
|
+
- **Naming Strategies**: Effortlessly translate between TypeScript `CamelCase` and database `snake_case` (or any custom format) with a pluggable system.
|
|
39
|
+
- **Built-in Serialization**: A centralized task queue and `@Serialized()` decorator ensure database operations are thread-safe and race-condition free by default.
|
|
40
|
+
- **Database Migrations**: Integrated migration system for version-controlled schema management and auto-generation from entities.
|
|
41
|
+
- **High Performance**: Optimized "Sticky Connections" and human-readable, minimal SQL generation.
|
|
42
|
+
- **Modern Architecture**: Pure `ESM` support, designed for `Node.js`, `Bun`, `Deno`, and even mobile/browser environments.
|
|
43
|
+
- **Rich Feature Set**: [Soft-delete](https://uql.app/docs/entities-soft-delete), [virtual fields](https://uql.app/docs/entities-virtual-fields), [repositories](https://uql.app/docs/querying-repository), and automatic handling of `JSON`, `JSONB`, and `Vector` types.
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
## 1. Install
|
|
48
|
+
|
|
49
|
+
1. Install the core package:
|
|
50
|
+
|
|
51
|
+
```sh
|
|
52
|
+
npm install @uql/core
|
|
53
|
+
# or
|
|
54
|
+
bun add @uql/core
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
2. Install one of the specific adapters for your database:
|
|
58
|
+
|
|
59
|
+
| Database | Driver | UQL Adapter |
|
|
60
|
+
| ------------ | ---------------- | ---------------- |
|
|
61
|
+
| `PostgreSQL` | `pg` | `uql-postgres` |
|
|
62
|
+
| `SQLite` | `better-sqlite3` | `uql-sqlite` |
|
|
63
|
+
| `MariaDB` | `mariadb` | `uql-maria` |
|
|
64
|
+
| `MySQL` | `mysql2` | `uql-mysql` |
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
For example, for `Postgres` install the `pg` driver:
|
|
69
|
+
|
|
70
|
+
```sh
|
|
71
|
+
npm install pg
|
|
72
|
+
# or
|
|
73
|
+
bun add pg
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
3. Additionally, your `tsconfig.json` may need the following flags:
|
|
77
|
+
|
|
78
|
+
```json
|
|
79
|
+
"target": "es2022",
|
|
80
|
+
"experimentalDecorators": true,
|
|
81
|
+
"emitDecoratorMetadata": true
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
> **Note**: UQL provides first-class support for **Bun**. It is recommended to use Bun for a significantly faster developer experience.
|
|
87
|
+
|
|
88
|
+
---
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
## 2. Define the entities
|
|
93
|
+
|
|
94
|
+
Annotate your classes with decorators from `uql/entity`. UQL supports detailed schema metadata for precise DDL generation.
|
|
95
|
+
|
|
96
|
+
```ts
|
|
97
|
+
import { Entity, Id, Field, OneToOne, OneToMany, ManyToOne, ManyToMany } from '@uql/core/entity';
|
|
98
|
+
import type { Relation } from '@uql/core/type';
|
|
99
|
+
|
|
100
|
+
@Entity()
|
|
101
|
+
export class User {
|
|
102
|
+
@Id()
|
|
103
|
+
id?: string;
|
|
104
|
+
|
|
105
|
+
@Field({ length: 100, index: true })
|
|
106
|
+
name?: string;
|
|
107
|
+
|
|
108
|
+
@Field({ unique: true, comment: 'User login email' })
|
|
109
|
+
email?: string;
|
|
110
|
+
|
|
111
|
+
@OneToOne({ entity: () => Profile, mappedBy: 'user', cascade: true })
|
|
112
|
+
profile?: Relation<Profile>; // Relation<T> handles circular dependencies
|
|
113
|
+
|
|
114
|
+
@OneToMany({ entity: () => Post, mappedBy: 'author' })
|
|
115
|
+
posts?: Relation<Post>[];
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
@Entity()
|
|
119
|
+
export class Profile {
|
|
120
|
+
@Id()
|
|
121
|
+
id?: string;
|
|
122
|
+
|
|
123
|
+
@Field()
|
|
124
|
+
bio?: string;
|
|
125
|
+
|
|
126
|
+
@Field({ reference: () => User })
|
|
127
|
+
userId?: string;
|
|
128
|
+
|
|
129
|
+
@OneToOne({ entity: () => User })
|
|
130
|
+
user?: User;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
@Entity()
|
|
134
|
+
export class Post {
|
|
135
|
+
@Id()
|
|
136
|
+
id?: number;
|
|
137
|
+
|
|
138
|
+
@Field()
|
|
139
|
+
title?: string;
|
|
140
|
+
|
|
141
|
+
@Field({ reference: () => User })
|
|
142
|
+
authorId?: string;
|
|
143
|
+
|
|
144
|
+
@ManyToOne({ entity: () => User })
|
|
145
|
+
author?: User;
|
|
146
|
+
|
|
147
|
+
@ManyToMany({ entity: () => Tag, through: () => PostTag })
|
|
148
|
+
tags?: Tag[];
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
@Entity()
|
|
152
|
+
export class Tag {
|
|
153
|
+
@Id()
|
|
154
|
+
id?: string;
|
|
155
|
+
|
|
156
|
+
@Field()
|
|
157
|
+
name?: string;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
@Entity()
|
|
161
|
+
export class PostTag {
|
|
162
|
+
@Id()
|
|
163
|
+
id?: string;
|
|
164
|
+
|
|
165
|
+
@Field({ reference: () => Post })
|
|
166
|
+
postId?: number;
|
|
167
|
+
|
|
168
|
+
@Field({ reference: () => Tag })
|
|
169
|
+
tagId?: string;
|
|
170
|
+
}
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
## 3. Setup a querier-pool
|
|
176
|
+
|
|
177
|
+
A querier-pool can be set in any of the bootstrap files of your app (e.g. in the `server.ts`).
|
|
178
|
+
|
|
179
|
+
```ts
|
|
180
|
+
// file: ./shared/orm.ts
|
|
181
|
+
import { SnakeCaseNamingStrategy } from '@uql/core';
|
|
182
|
+
import { PgQuerierPool } from 'uql-postgres';
|
|
183
|
+
|
|
184
|
+
export const querierPool = new PgQuerierPool(
|
|
185
|
+
{
|
|
186
|
+
host: 'localhost',
|
|
187
|
+
user: 'theUser',
|
|
188
|
+
password: 'thePassword',
|
|
189
|
+
database: 'theDatabase',
|
|
190
|
+
min: 1,
|
|
191
|
+
max: 10,
|
|
192
|
+
},
|
|
193
|
+
// Optional extra options.
|
|
194
|
+
{
|
|
195
|
+
// Optional, any custom logger function can be passed here (optional).
|
|
196
|
+
logger: console.debug,
|
|
197
|
+
// Automatically translate between TypeScript camelCase and database snake_case.
|
|
198
|
+
// This affects both queries and schema generation.
|
|
199
|
+
namingStrategy: new SnakeCaseNamingStrategy()
|
|
200
|
+
},
|
|
201
|
+
);
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
## 4. Manipulate the data
|
|
207
|
+
|
|
208
|
+
UQL provides multiple ways to interact with your data, from low-level `Queriers` to high-level `Repositories`.
|
|
209
|
+
|
|
210
|
+
### Using Repositories (Recommended)
|
|
211
|
+
|
|
212
|
+
Repositories provide a clean, Data-Mapper style interface for your entities.
|
|
213
|
+
|
|
214
|
+
```ts
|
|
215
|
+
import { GenericRepository } from '@uql/core/repository';
|
|
216
|
+
import { User } from './shared/models/index.js';
|
|
217
|
+
import { querierPool } from './shared/orm.js';
|
|
218
|
+
|
|
219
|
+
// Get a querier from the pool
|
|
220
|
+
const querier = await querierPool.getQuerier();
|
|
221
|
+
|
|
222
|
+
try {
|
|
223
|
+
const userRepository = new GenericRepository(User, querier);
|
|
224
|
+
|
|
225
|
+
// Advanced querying with relations and virtual fields
|
|
226
|
+
const users = await userRepository.findMany({
|
|
227
|
+
$select: {
|
|
228
|
+
id: true,
|
|
229
|
+
name: true,
|
|
230
|
+
profile: ['picture'], // Select specific fields from a 1-1 relation
|
|
231
|
+
tagsCount: true // Virtual field (calculated at runtime)
|
|
232
|
+
},
|
|
233
|
+
$where: {
|
|
234
|
+
email: { $iincludes: '@uql/core' }, // Case-insensitive search
|
|
235
|
+
status: 'active'
|
|
236
|
+
},
|
|
237
|
+
$sort: { createdAt: -1 },
|
|
238
|
+
$limit: 50
|
|
239
|
+
});
|
|
240
|
+
} finally {
|
|
241
|
+
// Always release the querier to the pool
|
|
242
|
+
await querier.release();
|
|
243
|
+
}
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
### Advanced: Deep Selection & Filtering
|
|
247
|
+
|
|
248
|
+
UQL's query syntax is context-aware. When you query a relation, the available fields and operators are automatically suggested and validated based on that related entity.
|
|
249
|
+
|
|
250
|
+
```ts
|
|
251
|
+
import { GenericRepository } from '@uql/core/repository';
|
|
252
|
+
import { User } from './shared/models/index.js';
|
|
253
|
+
import { querierPool } from './shared/orm.js';
|
|
254
|
+
|
|
255
|
+
const authorsWithPopularPosts = await querierPool.transaction(async (querier) => {
|
|
256
|
+
const userRepository = new GenericRepository(User, querier);
|
|
257
|
+
|
|
258
|
+
return userRepository.findMany({
|
|
259
|
+
$select: {
|
|
260
|
+
id: true,
|
|
261
|
+
name: true,
|
|
262
|
+
profile: {
|
|
263
|
+
$select: ['bio'],
|
|
264
|
+
// Filter related record and enforce INNER JOIN
|
|
265
|
+
$where: { bio: { $ne: null } },
|
|
266
|
+
$required: true
|
|
267
|
+
},
|
|
268
|
+
posts: {
|
|
269
|
+
$select: ['title', 'createdAt'],
|
|
270
|
+
// Filter the related collection directly
|
|
271
|
+
$where: { title: { $iincludes: 'typescript' } },
|
|
272
|
+
$sort: { createdAt: -1 },
|
|
273
|
+
$limit: 5
|
|
274
|
+
}
|
|
275
|
+
},
|
|
276
|
+
$where: {
|
|
277
|
+
name: { $istartsWith: 'a' }
|
|
278
|
+
}
|
|
279
|
+
});
|
|
280
|
+
});
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
### Advanced: Virtual Fields & Raw SQL
|
|
284
|
+
|
|
285
|
+
Define complex logic directly in your entities using `raw` functions from `uql/util`. These are highly efficient as they are resolved during SQL generation.
|
|
286
|
+
|
|
287
|
+
```ts
|
|
288
|
+
import { Entity, Id, Field } from '@uql/core/entity';
|
|
289
|
+
import { raw } from '@uql/core/util';
|
|
290
|
+
import { ItemTag } from './shared/models/index.js';
|
|
291
|
+
|
|
292
|
+
@Entity()
|
|
293
|
+
export class Item {
|
|
294
|
+
@Id()
|
|
295
|
+
id: number;
|
|
296
|
+
|
|
297
|
+
@Field()
|
|
298
|
+
name: string;
|
|
299
|
+
|
|
300
|
+
@Field({
|
|
301
|
+
virtual: raw(({ ctx, dialect, escapedPrefix }) => {
|
|
302
|
+
ctx.append('(');
|
|
303
|
+
dialect.count(ctx, ItemTag, {
|
|
304
|
+
$where: {
|
|
305
|
+
itemId: raw(({ ctx }) => ctx.append(`${escapedPrefix}.id`))
|
|
306
|
+
}
|
|
307
|
+
}, { autoPrefix: true });
|
|
308
|
+
ctx.append(')');
|
|
309
|
+
})
|
|
310
|
+
})
|
|
311
|
+
tagsCount?: number;
|
|
312
|
+
}
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
### Thread-Safe Transactions
|
|
316
|
+
|
|
317
|
+
UQL ensures your operations are serialized and thread-safe.
|
|
318
|
+
|
|
319
|
+
```ts
|
|
320
|
+
import { User, Profile } from './shared/models/index.js';
|
|
321
|
+
import { querierPool } from './shared/orm.js';
|
|
322
|
+
|
|
323
|
+
const result = await querierPool.transaction(async (querier) => {
|
|
324
|
+
const user = await querier.findOne(User, { $where: { email: '...' } });
|
|
325
|
+
const profileId = await querier.insertOne(Profile, { userId: user.id, ... });
|
|
326
|
+
return { userId: user.id, profileId };
|
|
327
|
+
});
|
|
328
|
+
// Connection is automatically released after transaction
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
|
|
332
|
+
|
|
333
|
+
## 5. Migrations & Synchronization
|
|
334
|
+
|
|
335
|
+
UQL includes a robust migration system and an "Entity-First" synchronization engine built directly into the core.
|
|
336
|
+
|
|
337
|
+
### 1. Create Configuration
|
|
338
|
+
|
|
339
|
+
Create a `uql.config.ts` file in your project root:
|
|
340
|
+
|
|
341
|
+
```typescript
|
|
342
|
+
import { PgQuerierPool } from 'uql-postgres';
|
|
343
|
+
import { User, Post } from './src/entities/index.js';
|
|
344
|
+
|
|
345
|
+
export default {
|
|
346
|
+
querierPool: new PgQuerierPool({ /* config */ }),
|
|
347
|
+
dialect: 'postgres',
|
|
348
|
+
entities: [User, Post],
|
|
349
|
+
migrationsPath: './migrations',
|
|
350
|
+
};
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
### 2. Manage via CLI
|
|
354
|
+
|
|
355
|
+
UQL provides a dedicated CLI tool for migrations.
|
|
356
|
+
|
|
357
|
+
```bash
|
|
358
|
+
# Generate a migration by comparing entities vs database
|
|
359
|
+
npx uql-migrate generate:entities initial_schema
|
|
360
|
+
# or
|
|
361
|
+
bunx uql-migrate generate:entities initial_schema
|
|
362
|
+
|
|
363
|
+
# Run pending migrations
|
|
364
|
+
npx uql-migrate up
|
|
365
|
+
# or
|
|
366
|
+
bunx uql-migrate up
|
|
367
|
+
|
|
368
|
+
# Rollback the last migration
|
|
369
|
+
npx uql-migrate down
|
|
370
|
+
# or
|
|
371
|
+
bunx uql-migrate down
|
|
372
|
+
|
|
373
|
+
# Check status
|
|
374
|
+
npx uql-migrate status
|
|
375
|
+
# or
|
|
376
|
+
bunx uql-migrate status
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
### 3. Entity-First Synchronization (Development)
|
|
380
|
+
|
|
381
|
+
In development, you can use `autoSync` to automatically keep your database in sync with your entities without manual migrations. It is **safe by default**, meaning it only adds missing tables and columns.
|
|
382
|
+
|
|
383
|
+
```ts
|
|
384
|
+
import { Migrator } from '@uql/core/migrate';
|
|
385
|
+
import { querierPool } from './shared/orm.js';
|
|
386
|
+
|
|
387
|
+
const migrator = new Migrator(querierPool);
|
|
388
|
+
await migrator.autoSync({ logging: true });
|
|
389
|
+
```
|
|
390
|
+
|
|
391
|
+
Check out the full [documentation](https://uql.app/docs/migrations) for detailed CLI commands and advanced usage.
|
|
392
|
+
|
|
393
|
+
|
|
394
|
+
|
|
395
|
+
Check out the full documentation at [uql.app](https://uql.app) for details on:
|
|
396
|
+
- [Complex Logical Operators](https://uql.app/docs/querying-logical-operators)
|
|
397
|
+
- [Relationship Mapping (1-1, 1-M, M-M)](https://uql.app/docs/querying-relations)
|
|
398
|
+
- [Soft Deletes & Auditing](https://uql.app/docs/entities-soft-delete)
|
|
399
|
+
- [Database Migration & Syncing](https://uql.app/docs/migrations)
|
|
400
|
+
|
|
401
|
+
---
|
|
402
|
+
|
|
403
|
+
## 🛠 Deep Dive: Tests & Technical Resources
|
|
404
|
+
|
|
405
|
+
For those who want to see the "engine under the hood," check out these resources in the source code:
|
|
406
|
+
|
|
407
|
+
- **Entity Mocks**: See how complex entities and virtual fields are defined in [entityMock.ts](https://github.com/rogerpadilla/uql/blob/main/packages/core/src/test/entityMock.ts).
|
|
408
|
+
- **Core Dialect Logic**: The foundation of our context-aware SQL generation in [abstractSqlDialect.ts](https://github.com/rogerpadilla/uql/blob/main/packages/core/src/dialect/abstractSqlDialect.ts).
|
|
409
|
+
- **Comprehensive Test Suite**:
|
|
410
|
+
- [Abstract SQL Spec](https://github.com/rogerpadilla/uql/blob/main/packages/core/src/dialect/abstractSqlDialect-spec.ts): The base test suite shared by all dialects.
|
|
411
|
+
- [PostgreSQL Spec](https://github.com/rogerpadilla/uql/blob/main/packages/postgres/src/postgresDialect.spec.ts) | [MySQL Spec](https://github.com/rogerpadilla/uql/blob/main/packages/mysql/src/mysqlDialect.spec.ts) | [SQLite Spec](https://github.com/rogerpadilla/uql/blob/main/packages/sqlite/src/sqliteDialect.spec.ts).
|
|
412
|
+
- [Querier Integration Tests](https://github.com/rogerpadilla/uql/blob/main/packages/core/src/querier/abstractSqlQuerier-spec.ts): Testing the interaction between SQL generation and connection management.
|
|
413
|
+
- [MongoDB Migration Tests](https://github.com/rogerpadilla/uql/blob/main/packages/uql/src/migrate/migrator-mongo.it.ts): Integration tests ensuring correct collection and index synchronization for MongoDB.
|
package/dist/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"homepage": "https://uql.app",
|
|
4
4
|
"description": "One Language. Frontend to Backend.",
|
|
5
5
|
"license": "MIT",
|
|
6
|
-
"version": "3.
|
|
6
|
+
"version": "3.1.0",
|
|
7
7
|
"type": "module",
|
|
8
8
|
"main": "./index.js",
|
|
9
9
|
"types": "./index.d.ts",
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
},
|
|
37
37
|
"sideEffects": false,
|
|
38
38
|
"scripts": {
|
|
39
|
-
"copyfiles": "copyfiles -f package.json
|
|
39
|
+
"copyfiles": "copyfiles -f package.json ../../README.md ../../CHANGELOG.md dist",
|
|
40
40
|
"compile.browser": "bunchee --clean false --no-dts ./src/browser/index.ts --sourcemap -o ./dist/browser/uql-browser.min.js",
|
|
41
41
|
"build": "bun run clean && tsc -b tsconfig.build.json && bun run compile.browser && bun run copyfiles",
|
|
42
42
|
"start": "tsc --watch",
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"homepage": "https://uql.app",
|
|
4
4
|
"description": "One Language. Frontend to Backend.",
|
|
5
5
|
"license": "MIT",
|
|
6
|
-
"version": "3.1.
|
|
6
|
+
"version": "3.1.1",
|
|
7
7
|
"type": "module",
|
|
8
8
|
"main": "./index.js",
|
|
9
9
|
"types": "./index.d.ts",
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
},
|
|
37
37
|
"sideEffects": false,
|
|
38
38
|
"scripts": {
|
|
39
|
-
"copyfiles": "copyfiles -f package.json
|
|
39
|
+
"copyfiles": "copyfiles -f package.json ../../README.md ../../CHANGELOG.md dist",
|
|
40
40
|
"compile.browser": "bunchee --clean false --no-dts ./src/browser/index.ts --sourcemap -o ./dist/browser/uql-browser.min.js",
|
|
41
41
|
"build": "bun run clean && tsc -b tsconfig.build.json && bun run compile.browser && bun run copyfiles",
|
|
42
42
|
"start": "tsc --watch",
|
|
@@ -127,5 +127,5 @@
|
|
|
127
127
|
"publishConfig": {
|
|
128
128
|
"access": "public"
|
|
129
129
|
},
|
|
130
|
-
"gitHead": "
|
|
130
|
+
"gitHead": "0105a041059fcfd55222bdf1d7ddb01fad984a36"
|
|
131
131
|
}
|