Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 42 additions & 0 deletions docs/api/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,48 @@ ObjectQL provides a **unified query protocol** that can be exposed through multi
4. **Secure**: Built-in validation, permission checks, SQL injection prevention
5. **Universal**: Same query works across MongoDB, PostgreSQL, SQLite

### Unified ID Field

ObjectQL uses a **unified `id` field** as the primary key across all database drivers:

- **Consistent Naming**: Always use `id` in API requests and responses
- **Database Agnostic**: Works seamlessly with both MongoDB (which uses `_id` internally) and SQL databases
- **Automatic Mapping**: MongoDB driver transparently converts between `id` (API) and `_id` (database)

**Example:**
```json
// Create with custom ID - works with any driver
{
"op": "create",
"object": "users",
"args": {
"id": "user-123",
"name": "Alice"
}
}

// Query by ID - works with any driver
{
"op": "find",
"object": "users",
"args": {
"filters": [["id", "=", "user-123"]]
}
}

// Response always uses 'id'
{
"data": [
{
"id": "user-123",
"name": "Alice"
}
]
}
```

See the [Driver Documentation](../guide/drivers/index.md) for more details.

---

## JSON-RPC Style API
Expand Down
26 changes: 26 additions & 0 deletions docs/guide/drivers/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,32 @@ We currently support the following official drivers:
* **[SQL Driver (Knex)](./sql)**: Supports PostgreSQL, MySQL, SQLite, MSSQL, etc.
* **[MongoDB Driver](./mongo)**: Supports MongoDB.

## Unified ID Field

ObjectQL provides a **consistent API** across all database drivers by standardizing on the `id` field name for primary keys:

- **MongoDB Driver**: Automatically maps `id` (API) ↔ `_id` (database)
- **SQL Driver**: Uses `id` natively in the database schema

This means you can write database-agnostic code:

```typescript
// Same code works with MongoDB OR SQL drivers!
const user = await app.create('users', {
id: 'user-123', // Works with both drivers
name: 'Alice'
});

const query = {
filters: [['id', '=', 'user-123']] // Consistent across drivers
};
const results = await app.find('users', query);
```

**No more switching between `_id` and `id` depending on your database!**

See the individual driver documentation for implementation details.

## Configuring a Driver

Drivers are instantiated and passed to the `ObjectQL` constructor under the `driver` property (or `datasources` map for multi-db setup).
Expand Down
124 changes: 121 additions & 3 deletions docs/guide/drivers/mongo.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,128 @@ const driver = new MongoDriver({
});
```

## IDs and `_id`
## Unified ID Field

MongoDB uses `_id` as the primary key. ObjectQL will map the standard `_id` field to the ObjectQL `_id` field.
When querying, ObjectQL handles the conversion between string IDs and `ObjectId` automatically.
ObjectQL provides a **unified `id` field** across all database drivers for a consistent API experience.

### How It Works

- **API Level**: You always use `id` in your application code (queries, filters, documents)
- **Database Level**: MongoDB internally uses `_id` as required by MongoDB conventions
- **Automatic Mapping**: The driver transparently converts between `id` and `_id`

### Examples

**Querying by ID:**
```typescript
// ✅ Use 'id' in queries - works consistently across all drivers
const query = {
filters: [['id', '=', '507f1f77bcf86cd799439011']]
};
const users = await app.find('users', query);
```

**Creating Documents:**
```typescript
// ✅ Use 'id' when creating - the driver maps it to '_id' internally
const newUser = await app.create('users', {
id: '507f1f77bcf86cd799439011', // Optional: specify custom ID
name: 'Alice',
email: 'alice@example.com'
});

// Result returned with 'id' field (not '_id')
console.log(newUser.id); // '507f1f77bcf86cd799439011'
```

**Finding by ID:**
```typescript
// ✅ Use 'id' parameter
const user = await app.findOne('users', '507f1f77bcf86cd799439011');
console.log(user.id); // Always 'id', never '_id'
```

**Sorting by ID:**
```typescript
const query = {
sort: [['id', 'desc']] // ✅ Use 'id' for sorting
};
const users = await app.find('users', query);
```

**Field Projection:**
```typescript
const query = {
fields: ['id', 'name', 'email'] // ✅ Use 'id' to select the ID field
};
const users = await app.find('users', query);
// Results contain 'id', not '_id'
```

### Migration from Legacy Code

If you have existing code using `_id`, you have two options:

1. **Recommended: Migrate to `id`** for consistency across drivers:

**Before (Legacy):**
```typescript
// ❌ Old way - inconsistent with SQL drivers
const query = {
filters: [['_id', '=', '507f1f77bcf86cd799439011']],
sort: [['_id', 'desc']],
fields: ['_id', 'name']
};
```

**After (Recommended):**
```typescript
// ✅ New way - consistent across all drivers
const query = {
filters: [['id', '=', '507f1f77bcf86cd799439011']],
sort: [['id', 'desc']],
fields: ['id', 'name']
};
```

2. **Backward Compatible Mode:** Continue using `_id` in queries (results still return `id`)

The MongoDB driver **fully supports `_id` in filters, sorting, and field projections** for backward compatibility:

```typescript
// ✅ This works - backward compatible
const query = {
filters: [['_id', '=', '507f1f77bcf86cd799439011']],
sort: [['_id', 'desc']],
fields: ['_id', 'name']
};
const users = await app.find('users', query);

// Results ALWAYS use 'id' regardless of query field name
console.log(users[0].id); // '507f1f77bcf86cd799439011'
console.log(users[0]._id); // undefined
```

**Key Points:**
- Queries accept both `id` and `_id` (automatically mapped to MongoDB's `_id`)
- Results always return `id` field (never `_id`)
- Using `id` is recommended for database portability

## ID Generation

When creating documents without specifying an `id`, the driver automatically generates a string ID:

```typescript
const newUser = await app.create('users', {
name: 'Bob',
// No id specified
});

// Driver generates a unique string ID (not ObjectId)
console.log(newUser.id); // e.g., '507f1f77bcf86cd799439011'
```

The generated IDs are hexadecimal strings (24 characters) that maintain MongoDB's uniqueness guarantees without using ObjectId objects.

## Limitations

Expand Down
68 changes: 68 additions & 0 deletions docs/guide/drivers/sql.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,74 @@ const driver = new KnexDriver({
});
```

## Unified ID Field

ObjectQL uses **`id`** as the primary key field name across all SQL databases for consistency with the MongoDB driver.

### How It Works

- **Database Column**: SQL tables use `id` as the primary key column (VARCHAR/TEXT type)
- **API Level**: You use `id` in your queries, filters, and documents
- **Consistency**: Same API as MongoDB driver - no need to remember `_id` vs `id`

### Examples

**Table Schema:**
```sql
CREATE TABLE users (
id VARCHAR(255) PRIMARY KEY, -- Primary key is 'id'
name VARCHAR(255),
email VARCHAR(255),
created_at TIMESTAMP,
updated_at TIMESTAMP
);
```

**Creating Documents:**
```typescript
// Create with auto-generated ID
const user = await app.create('users', {
name: 'Alice',
email: 'alice@example.com'
});
console.log(user.id); // Auto-generated UUID or custom ID

// Create with custom ID
const user = await app.create('users', {
id: 'custom-user-123',
name: 'Bob'
});
```

**Querying by ID:**
```typescript
const query = {
filters: [['id', '=', 'custom-user-123']]
};
const users = await app.find('users', query);
```

**Finding by ID:**
```typescript
const user = await app.findOne('users', 'custom-user-123');
```

### Legacy `_id` Support

For backward compatibility, if you provide `_id` in a create operation, the driver will automatically map it to `id`:

```typescript
// Legacy code - automatically mapped
const user = await app.create('users', {
_id: 'user-123', // Mapped to 'id' internally
name: 'Charlie'
});

console.log(user.id); // 'user-123'
```

This ensures smooth migration for projects that previously used MongoDB-style `_id` fields.

## Schema Mapping

The driver automatically maps ObjectQL types to SQL column types:
Expand Down
Loading