Skip to content

Commit 821e1b6

Browse files
authored
Merge pull request #27 from objectql/copilot/unify-api-id-usage
2 parents 64c90be + dced650 commit 821e1b6

7 files changed

Lines changed: 772 additions & 31 deletions

File tree

docs/api/README.md

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,48 @@ ObjectQL provides a **unified query protocol** that can be exposed through multi
3838
4. **Secure**: Built-in validation, permission checks, SQL injection prevention
3939
5. **Universal**: Same query works across MongoDB, PostgreSQL, SQLite
4040

41+
### Unified ID Field
42+
43+
ObjectQL uses a **unified `id` field** as the primary key across all database drivers:
44+
45+
- **Consistent Naming**: Always use `id` in API requests and responses
46+
- **Database Agnostic**: Works seamlessly with both MongoDB (which uses `_id` internally) and SQL databases
47+
- **Automatic Mapping**: MongoDB driver transparently converts between `id` (API) and `_id` (database)
48+
49+
**Example:**
50+
```json
51+
// Create with custom ID - works with any driver
52+
{
53+
"op": "create",
54+
"object": "users",
55+
"args": {
56+
"id": "user-123",
57+
"name": "Alice"
58+
}
59+
}
60+
61+
// Query by ID - works with any driver
62+
{
63+
"op": "find",
64+
"object": "users",
65+
"args": {
66+
"filters": [["id", "=", "user-123"]]
67+
}
68+
}
69+
70+
// Response always uses 'id'
71+
{
72+
"data": [
73+
{
74+
"id": "user-123",
75+
"name": "Alice"
76+
}
77+
]
78+
}
79+
```
80+
81+
See the [Driver Documentation](../guide/drivers/index.md) for more details.
82+
4183
---
4284

4385
## JSON-RPC Style API

docs/guide/drivers/index.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,32 @@ We currently support the following official drivers:
1111
* **[SQL Driver (Knex)](./sql)**: Supports PostgreSQL, MySQL, SQLite, MSSQL, etc.
1212
* **[MongoDB Driver](./mongo)**: Supports MongoDB.
1313

14+
## Unified ID Field
15+
16+
ObjectQL provides a **consistent API** across all database drivers by standardizing on the `id` field name for primary keys:
17+
18+
- **MongoDB Driver**: Automatically maps `id` (API) ↔ `_id` (database)
19+
- **SQL Driver**: Uses `id` natively in the database schema
20+
21+
This means you can write database-agnostic code:
22+
23+
```typescript
24+
// Same code works with MongoDB OR SQL drivers!
25+
const user = await app.create('users', {
26+
id: 'user-123', // Works with both drivers
27+
name: 'Alice'
28+
});
29+
30+
const query = {
31+
filters: [['id', '=', 'user-123']] // Consistent across drivers
32+
};
33+
const results = await app.find('users', query);
34+
```
35+
36+
**No more switching between `_id` and `id` depending on your database!**
37+
38+
See the individual driver documentation for implementation details.
39+
1440
## Configuring a Driver
1541

1642
Drivers are instantiated and passed to the `ObjectQL` constructor under the `driver` property (or `datasources` map for multi-db setup).

docs/guide/drivers/mongo.md

Lines changed: 121 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,128 @@ const driver = new MongoDriver({
2525
});
2626
```
2727

28-
## IDs and `_id`
28+
## Unified ID Field
2929

30-
MongoDB uses `_id` as the primary key. ObjectQL will map the standard `_id` field to the ObjectQL `_id` field.
31-
When querying, ObjectQL handles the conversion between string IDs and `ObjectId` automatically.
30+
ObjectQL provides a **unified `id` field** across all database drivers for a consistent API experience.
31+
32+
### How It Works
33+
34+
- **API Level**: You always use `id` in your application code (queries, filters, documents)
35+
- **Database Level**: MongoDB internally uses `_id` as required by MongoDB conventions
36+
- **Automatic Mapping**: The driver transparently converts between `id` and `_id`
37+
38+
### Examples
39+
40+
**Querying by ID:**
41+
```typescript
42+
// ✅ Use 'id' in queries - works consistently across all drivers
43+
const query = {
44+
filters: [['id', '=', '507f1f77bcf86cd799439011']]
45+
};
46+
const users = await app.find('users', query);
47+
```
48+
49+
**Creating Documents:**
50+
```typescript
51+
// ✅ Use 'id' when creating - the driver maps it to '_id' internally
52+
const newUser = await app.create('users', {
53+
id: '507f1f77bcf86cd799439011', // Optional: specify custom ID
54+
name: 'Alice',
55+
email: 'alice@example.com'
56+
});
57+
58+
// Result returned with 'id' field (not '_id')
59+
console.log(newUser.id); // '507f1f77bcf86cd799439011'
60+
```
61+
62+
**Finding by ID:**
63+
```typescript
64+
// ✅ Use 'id' parameter
65+
const user = await app.findOne('users', '507f1f77bcf86cd799439011');
66+
console.log(user.id); // Always 'id', never '_id'
67+
```
68+
69+
**Sorting by ID:**
70+
```typescript
71+
const query = {
72+
sort: [['id', 'desc']] // ✅ Use 'id' for sorting
73+
};
74+
const users = await app.find('users', query);
75+
```
76+
77+
**Field Projection:**
78+
```typescript
79+
const query = {
80+
fields: ['id', 'name', 'email'] // ✅ Use 'id' to select the ID field
81+
};
82+
const users = await app.find('users', query);
83+
// Results contain 'id', not '_id'
84+
```
85+
86+
### Migration from Legacy Code
87+
88+
If you have existing code using `_id`, you have two options:
89+
90+
1. **Recommended: Migrate to `id`** for consistency across drivers:
91+
92+
**Before (Legacy):**
93+
```typescript
94+
// ❌ Old way - inconsistent with SQL drivers
95+
const query = {
96+
filters: [['_id', '=', '507f1f77bcf86cd799439011']],
97+
sort: [['_id', 'desc']],
98+
fields: ['_id', 'name']
99+
};
100+
```
101+
102+
**After (Recommended):**
103+
```typescript
104+
// ✅ New way - consistent across all drivers
105+
const query = {
106+
filters: [['id', '=', '507f1f77bcf86cd799439011']],
107+
sort: [['id', 'desc']],
108+
fields: ['id', 'name']
109+
};
110+
```
111+
112+
2. **Backward Compatible Mode:** Continue using `_id` in queries (results still return `id`)
113+
114+
The MongoDB driver **fully supports `_id` in filters, sorting, and field projections** for backward compatibility:
115+
116+
```typescript
117+
// ✅ This works - backward compatible
118+
const query = {
119+
filters: [['_id', '=', '507f1f77bcf86cd799439011']],
120+
sort: [['_id', 'desc']],
121+
fields: ['_id', 'name']
122+
};
123+
const users = await app.find('users', query);
124+
125+
// Results ALWAYS use 'id' regardless of query field name
126+
console.log(users[0].id); // '507f1f77bcf86cd799439011'
127+
console.log(users[0]._id); // undefined
128+
```
129+
130+
**Key Points:**
131+
- Queries accept both `id` and `_id` (automatically mapped to MongoDB's `_id`)
132+
- Results always return `id` field (never `_id`)
133+
- Using `id` is recommended for database portability
134+
135+
## ID Generation
136+
137+
When creating documents without specifying an `id`, the driver automatically generates a string ID:
138+
139+
```typescript
140+
const newUser = await app.create('users', {
141+
name: 'Bob',
142+
// No id specified
143+
});
144+
145+
// Driver generates a unique string ID (not ObjectId)
146+
console.log(newUser.id); // e.g., '507f1f77bcf86cd799439011'
147+
```
148+
149+
The generated IDs are hexadecimal strings (24 characters) that maintain MongoDB's uniqueness guarantees without using ObjectId objects.
32150

33151
## Limitations
34152

docs/guide/drivers/sql.md

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,74 @@ const driver = new KnexDriver({
4444
});
4545
```
4646

47+
## Unified ID Field
48+
49+
ObjectQL uses **`id`** as the primary key field name across all SQL databases for consistency with the MongoDB driver.
50+
51+
### How It Works
52+
53+
- **Database Column**: SQL tables use `id` as the primary key column (VARCHAR/TEXT type)
54+
- **API Level**: You use `id` in your queries, filters, and documents
55+
- **Consistency**: Same API as MongoDB driver - no need to remember `_id` vs `id`
56+
57+
### Examples
58+
59+
**Table Schema:**
60+
```sql
61+
CREATE TABLE users (
62+
id VARCHAR(255) PRIMARY KEY, -- Primary key is 'id'
63+
name VARCHAR(255),
64+
email VARCHAR(255),
65+
created_at TIMESTAMP,
66+
updated_at TIMESTAMP
67+
);
68+
```
69+
70+
**Creating Documents:**
71+
```typescript
72+
// Create with auto-generated ID
73+
const user = await app.create('users', {
74+
name: 'Alice',
75+
email: 'alice@example.com'
76+
});
77+
console.log(user.id); // Auto-generated UUID or custom ID
78+
79+
// Create with custom ID
80+
const user = await app.create('users', {
81+
id: 'custom-user-123',
82+
name: 'Bob'
83+
});
84+
```
85+
86+
**Querying by ID:**
87+
```typescript
88+
const query = {
89+
filters: [['id', '=', 'custom-user-123']]
90+
};
91+
const users = await app.find('users', query);
92+
```
93+
94+
**Finding by ID:**
95+
```typescript
96+
const user = await app.findOne('users', 'custom-user-123');
97+
```
98+
99+
### Legacy `_id` Support
100+
101+
For backward compatibility, if you provide `_id` in a create operation, the driver will automatically map it to `id`:
102+
103+
```typescript
104+
// Legacy code - automatically mapped
105+
const user = await app.create('users', {
106+
_id: 'user-123', // Mapped to 'id' internally
107+
name: 'Charlie'
108+
});
109+
110+
console.log(user.id); // 'user-123'
111+
```
112+
113+
This ensures smooth migration for projects that previously used MongoDB-style `_id` fields.
114+
47115
## Schema Mapping
48116

49117
The driver automatically maps ObjectQL types to SQL column types:

0 commit comments

Comments
 (0)