ObjectQL API Reference
Version: 1.0.0
This document provides a comprehensive reference for all ObjectQL API interfaces. Given the extensive metadata capabilities of ObjectQL, we provide multiple API styles to suit different use cases.
Table of Contents
- API Overview
- JSON-RPC Style API
- REST-Style API
- Metadata API
- WebSocket API
- Authentication & Authorization
- Error Handling
- Rate Limiting
- Examples
API Overview
ObjectQL provides a unified query protocol that can be exposed through multiple API styles:
| API Style | Use Case | Endpoint Pattern |
|---|---|---|
| JSON-RPC | Universal client, AI agents, microservices | POST /api/objectql |
| REST | Traditional web apps, mobile apps | GET/POST/PUT/DELETE /api/data/:object |
| Metadata | Admin interfaces, schema discovery, runtime config | GET /api/metadata/* |
| GraphQL | Modern frontends with complex data requirements | POST /api/graphql (Planned) |
| WebSocket | Real-time apps, live updates | ws://host/api/realtime (Planned) |
Design Principles
- Protocol-First: All APIs accept/return structured JSON, never raw SQL
- Type-Safe: Full TypeScript definitions for all requests/responses
- AI-Friendly: Queries include optional
ai_contextfor explainability - Secure: Built-in validation, permission checks, SQL injection prevention
- Universal: Same query works across MongoDB, PostgreSQL, SQLite
JSON-RPC Style API
The primary ObjectQL API is a JSON-RPC style protocol where all operations are sent to a single endpoint.
Base Endpoint
POST /api/objectql
Content-Type: application/jsonRequest Format
interface ObjectQLRequest {
// Authentication context (optional, can also come from headers)
user?: {
id: string;
roles: string[];
[key: string]: any;
};
// The operation to perform
op: 'find' | 'findOne' | 'create' | 'update' | 'delete' | 'count' | 'action';
// The target object/table
object: string;
// Operation-specific arguments
args: any;
}Response Format
interface ObjectQLResponse {
data?: any;
error?: {
code: string;
message: string;
}
}Operations
1. find - Query Records
Retrieve multiple records with filtering, sorting, pagination, and joins.
Request:
{
"op": "find",
"object": "orders",
"args": {
"fields": ["order_no", "amount", "status", "created_at"],
"filters": [
["status", "=", "paid"],
"and",
["amount", ">", 1000]
],
"sort": [["created_at", "desc"]],
"top": 20,
"skip": 0,
"expand": {
"customer": {
"fields": ["name", "email"]
}
}
}
}Response:
{
"data": [
{
"order_no": "ORD-001",
"amount": 1500,
"status": "paid",
"created_at": "2024-01-15T10:30:00Z",
"customer": {
"name": "Acme Corp",
"email": "contact@acme.com"
}
}
]
}2. findOne - Get Single Record
Retrieve a single record by ID or query.
Request (by ID):
{
"op": "findOne",
"object": "users",
"args": "user_123"
}Request (by query):
{
"op": "findOne",
"object": "users",
"args": {
"filters": [["email", "=", "alice@example.com"]]
}
}Response:
{
"data": {
"id": "user_123",
"name": "Alice",
"email": "alice@example.com"
}
}3. create - Create Record
Insert a new record.
Request:
{
"op": "create",
"object": "tasks",
"args": {
"name": "Review PR",
"priority": "high",
"assignee_id": "user_123",
"due_date": "2024-01-20"
}
}Response:
{
"data": {
"id": "task_456",
"name": "Review PR",
"priority": "high",
"assignee_id": "user_123",
"due_date": "2024-01-20",
"created_at": "2024-01-15T10:30:00Z"
}
}4. update - Update Record
Modify an existing record.
Request:
{
"op": "update",
"object": "tasks",
"args": {
"id": "task_456",
"data": {
"status": "completed",
"completed_at": "2024-01-16T14:00:00Z"
}
}
}Response:
{
"data": {
"id": "task_456",
"status": "completed",
"completed_at": "2024-01-16T14:00:00Z"
}
}5. delete - Delete Record
Remove a record by ID.
Request:
{
"op": "delete",
"object": "tasks",
"args": {
"id": "task_456"
}
}Response:
{
"data": {
"id": "task_456",
"deleted": true
}
}6. count - Count Records
Get the count of records matching a filter.
Request:
{
"op": "count",
"object": "orders",
"args": {
"filters": [
["status", "=", "pending"]
]
}
}Response:
{
"data": 42
}7. action - Execute Custom Action
Execute a custom server-side action (RPC-style operation).
Request:
{
"op": "action",
"object": "orders",
"args": {
"action": "approve",
"id": "order_789",
"input": {
"approved_by": "manager_123",
"notes": "Approved for expedited shipping"
}
}
}Response:
{
"data": {
"success": true,
"message": "Order approved successfully",
"order": {
"id": "order_789",
"status": "approved",
"approved_at": "2024-01-15T10:30:00Z"
}
}
}Advanced Query Features
AI Context (Optional)
Add semantic information to queries for better logging, debugging, and AI processing:
{
"op": "find",
"object": "projects",
"ai_context": {
"intent": "Find at-risk projects requiring immediate attention",
"natural_language": "Show active projects that are overdue or over budget",
"use_case": "Project manager dashboard"
},
"args": {
"filters": [
["status", "=", "active"],
"and",
[
["end_date", "<", "$today"],
"or",
["actual_cost", ">", "budget"]
]
]
}
}Aggregation Queries
Perform GROUP BY operations:
{
"op": "find",
"object": "orders",
"args": {
"groupBy": ["category"],
"aggregate": [
{
"func": "sum",
"field": "amount",
"alias": "total_sales"
},
{
"func": "count",
"field": "id",
"alias": "order_count"
}
],
"filters": [["status", "=", "paid"]],
"sort": [["total_sales", "desc"]]
}
}REST-Style API
For traditional REST clients, ObjectQL can expose a REST-style interface.
Endpoints
| Method | Path | Description |
|---|---|---|
GET | /api/data/:object | List records |
GET | /api/data/:object/:id | Get single record |
POST | /api/data/:object | Create record |
PUT | /api/data/:object/:id | Update record |
DELETE | /api/data/:object/:id | Delete record |
List Records
GET /api/data/users?filter={"status":"active"}&sort=created_at&limit=20Response:
{
"data": [...],
"meta": {
"total": 150,
"page": 1,
"per_page": 20
}
}Get Single Record
GET /api/data/users/user_123Response:
{
"data": {
"id": "user_123",
"name": "Alice",
"email": "alice@example.com"
}
}Create Record
POST /api/data/users
Content-Type: application/json
{
"name": "Bob",
"email": "bob@example.com",
"role": "admin"
}Response:
{
"data": {
"id": "user_456",
"name": "Bob",
"email": "bob@example.com",
"role": "admin",
"created_at": "2024-01-15T10:30:00Z"
}
}Update Record
PUT /api/data/users/user_456
Content-Type: application/json
{
"role": "user"
}Response:
{
"data": {
"id": "user_456",
"role": "user",
"updated_at": "2024-01-15T11:00:00Z"
}
}Delete Record
DELETE /api/data/users/user_456Response:
{
"data": {
"id": "user_456",
"deleted": true
}
}Metadata API
The Metadata API provides runtime access to schema information, object definitions, and configuration.
Base Endpoint
/api/metadataEndpoints
1. List All Objects
Get a list of all registered objects/tables.
GET /api/metadata/objectsResponse:
{
"objects": [
{
"name": "users",
"label": "Users",
"icon": "user",
"description": "System users and authentication",
"fields": {...}
},
{
"name": "orders",
"label": "Orders",
"icon": "shopping-cart",
"description": "Customer orders",
"fields": {...}
}
]
}2. Get Object Schema
Get detailed schema for a specific object.
GET /api/metadata/objects/usersResponse:
{
"name": "users",
"label": "Users",
"icon": "user",
"description": "System users and authentication",
"fields": [
{
"name": "email",
"type": "email",
"label": "Email Address",
"required": true,
"unique": true
},
{
"name": "role",
"type": "select",
"label": "Role",
"options": ["admin", "user", "guest"],
"defaultValue": "user"
}
],
"actions": [
{
"name": "reset_password",
"type": "record",
"label": "Reset Password"
}
],
"hooks": [
{
"event": "afterCreate",
"description": "Send welcome email"
}
]
}3. Update Metadata (Admin)
Dynamically update object configuration at runtime.
PUT /api/metadata/object/users
Content-Type: application/json
Authorization: Bearer <admin_token>
{
"label": "System Users",
"description": "Updated description"
}Response:
{
"success": true
}4. Get Field Metadata
Get detailed information about a specific field.
GET /api/metadata/objects/users/fields/emailResponse:
{
"name": "email",
"type": "email",
"label": "Email Address",
"required": true,
"unique": true,
"validations": [
{
"type": "email_format",
"message": "Must be a valid email address"
}
]
}5. List Actions
Get all custom actions for an object.
GET /api/metadata/objects/orders/actionsResponse:
{
"actions": [
{
"name": "approve",
"type": "record",
"label": "Approve Order",
"params": {
"notes": {
"type": "textarea",
"label": "Approval Notes"
}
}
},
{
"name": "bulk_import",
"type": "global",
"label": "Bulk Import Orders"
}
]
}WebSocket API
(Planned Feature)
For real-time updates and live data synchronization.
Connection
const ws = new WebSocket('ws://localhost:3000/api/realtime');
ws.onopen = () => {
// Authenticate
ws.send(JSON.stringify({
type: 'auth',
token: 'your_jwt_token'
}));
// Subscribe to changes
ws.send(JSON.stringify({
type: 'subscribe',
object: 'orders',
filters: [["status", "=", "pending"]]
}));
};
ws.onmessage = (event) => {
const data = JSON.parse(event.data);
if (data.type === 'change') {
console.log('Record changed:', data.record);
}
};Authentication & Authorization
Authentication Methods
ObjectQL supports multiple authentication strategies:
1. JWT Tokens (Recommended)
POST /api/objectql
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
Content-Type: application/json2. API Keys
POST /api/objectql
X-API-Key: your_api_key_here
Content-Type: application/json3. Session Cookies
POST /api/objectql
Cookie: session_id=abc123...
Content-Type: application/json4. User Context in Request (Development Only)
For testing and development, you can pass user context directly in the request:
{
"user": {
"id": "user_123",
"roles": ["admin"]
},
"op": "find",
"object": "users",
"args": {}
}⚠️ Warning: In production, always authenticate via headers, not request body.
Permission System
ObjectQL enforces permissions at multiple levels:
- Object-Level: Can the user access this object at all?
- Operation-Level: Can they perform this operation (read/create/update/delete)?
- Field-Level: Which fields can they see/edit?
- Record-Level: Which specific records can they access?
Permission Check Flow:
Request → Authentication → Object Permission → Field Permission → Record Permission → ExecuteExample Permission Config:
# user.object.yml
permissions:
- profile: admin
allow_read: true
allow_create: true
allow_edit: true
allow_delete: true
- profile: user
allow_read: true
allow_create: false
allow_edit: true
allow_delete: false
record_filters:
- ["owner", "=", "$current_user"]Error Handling
Error Response Format
All errors follow a consistent format:
{
"error": {
"code": "ERROR_CODE",
"message": "Human-readable error message",
"details": {
"field": "email",
"reason": "Email already exists"
}
}
}Error Codes
| Code | HTTP Status | Description |
|---|---|---|
INVALID_REQUEST | 400 | Malformed request body |
VALIDATION_ERROR | 400 | Data validation failed |
UNAUTHORIZED | 401 | Authentication required |
FORBIDDEN | 403 | Insufficient permissions |
NOT_FOUND | 404 | Object or record not found |
CONFLICT | 409 | Unique constraint violation |
INTERNAL_ERROR | 500 | Server error |
DATABASE_ERROR | 500 | Database operation failed |
Example Error Responses
Validation Error:
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Validation failed",
"details": {
"fields": {
"email": "Invalid email format",
"age": "Must be greater than 0"
}
}
}
}Permission Error:
{
"error": {
"code": "FORBIDDEN",
"message": "You do not have permission to access this resource",
"details": {
"required_permission": "users:delete",
"user_roles": ["user"]
}
}
}Not Found:
{
"error": {
"code": "NOT_FOUND",
"message": "Object 'xyz' not found"
}
}Rate Limiting
ObjectQL supports configurable rate limiting to prevent abuse.
Default Limits
| Tier | Requests/Minute | Requests/Hour |
|---|---|---|
| Anonymous | 20 | 100 |
| Authenticated | 100 | 1000 |
| Premium | 500 | 10000 |
Rate Limit Headers
All responses include rate limit information:
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 95
X-RateLimit-Reset: 1642258800Rate Limit Exceeded
When rate limit is exceeded:
{
"error": {
"code": "RATE_LIMIT_EXCEEDED",
"message": "Too many requests. Please try again later.",
"details": {
"retry_after": 60
}
}
}HTTP Status: 429 Too Many Requests
Examples
Example 1: User Registration Flow
// 1. Create user
const response = await fetch('/api/objectql', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
op: 'create',
object: 'users',
args: {
email: 'alice@example.com',
name: 'Alice',
password_hash: 'hashed_password'
}
})
});
const { data: user } = await response.json();
// { id: 'user_123', email: 'alice@example.com', ... }
// 2. Send verification email (triggered by hook)
// 3. User verifies email via action
await fetch('/api/objectql', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
op: 'action',
object: 'users',
args: {
action: 'verify_email',
id: user.id,
input: {
token: 'verification_token_xyz'
}
}
})
});Example 2: Dashboard Analytics
// Get sales metrics for dashboard
const response = await fetch('/api/objectql', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + jwt_token
},
body: JSON.stringify({
op: 'find',
object: 'orders',
ai_context: {
intent: "Calculate monthly sales by category",
use_case: "Executive dashboard"
},
args: {
groupBy: ['category', 'month'],
aggregate: [
{ func: 'sum', field: 'amount', alias: 'revenue' },
{ func: 'count', field: 'id', alias: 'order_count' },
{ func: 'avg', field: 'amount', alias: 'avg_order_value' }
],
filters: [
['status', '=', 'paid'],
'and',
['created_at', '>=', '2024-01-01']
],
sort: [['month', 'asc'], ['revenue', 'desc']]
}
})
});
const { data } = await response.json();
// [
// { category: 'Electronics', month: '2024-01', revenue: 50000, order_count: 120, avg_order_value: 416.67 },
// { category: 'Clothing', month: '2024-01', revenue: 30000, order_count: 250, avg_order_value: 120.00 },
// ...
// ]Example 3: Complex Search with Relations
// Find customers with high-value recent orders
const response = await fetch('/api/objectql', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + jwt_token
},
body: JSON.stringify({
op: 'find',
object: 'customers',
args: {
fields: ['name', 'email', 'vip_level', 'total_spent'],
filters: [
['vip_level', '>=', 'gold'],
'and',
['is_active', '=', true]
],
expand: {
orders: {
fields: ['order_no', 'amount', 'status'],
filters: [
['created_at', '>', '2024-01-01'],
'and',
['amount', '>', 1000]
],
sort: [['created_at', 'desc']],
top: 5
}
},
sort: [['total_spent', 'desc']],
top: 20
}
})
});Example 4: Bulk Operations
// Create multiple records in one request
const response = await fetch('/api/objectql', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
op: 'action',
object: 'tasks',
args: {
action: 'bulk_create',
input: {
items: [
{ name: 'Task 1', priority: 'high' },
{ name: 'Task 2', priority: 'medium' },
{ name: 'Task 3', priority: 'low' }
]
}
}
})
});Example 5: Metadata-Driven Form Generation
// 1. Fetch object schema
const schemaResponse = await fetch('/api/metadata/objects/contacts');
const schema = await schemaResponse.json();
// 2. Generate form based on field metadata
const form = generateForm(schema.fields);
// 3. Submit form data
const submitResponse = await fetch('/api/objectql', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
op: 'create',
object: 'contacts',
args: formData
})
});OpenAPI/Swagger Specification
ObjectQL automatically generates OpenAPI 3.0 specifications from your metadata.
Access the Spec
GET /api/objectql/openapi.jsonThis can be imported into:
- Swagger UI for interactive API documentation
- Postman for testing
- Code Generators for client SDKs
Example Integration
<!-- Swagger UI -->
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="https://unpkg.com/swagger-ui-dist/swagger-ui.css" />
</head>
<body>
<div id="swagger-ui"></div>
<script src="https://unpkg.com/swagger-ui-dist/swagger-ui-bundle.js"></script>
<script>
SwaggerUIBundle({
url: '/api/objectql/openapi.json',
dom_id: '#swagger-ui'
});
</script>
</body>
</html>Best Practices
1. Use Specific Field Projections
❌ Don't fetch all fields:
{
"op": "find",
"object": "users"
}✅ Do specify only needed fields:
{
"op": "find",
"object": "users",
"args": {
"fields": ["id", "name", "email"]
}
}2. Use Pagination for Large Datasets
{
"op": "find",
"object": "orders",
"args": {
"top": 50,
"skip": 0
}
}3. Add Indexes for Filtered Fields
If you frequently filter by a field, add an index:
# order.object.yml
indexes:
- fields: [status, created_at]4. Use AI Context for Complex Queries
{
"op": "find",
"object": "projects",
"ai_context": {
"intent": "Find at-risk projects",
"natural_language": "Active projects that are overdue or over budget"
},
"args": {...}
}5. Batch Related Requests
Use expand instead of multiple requests:
❌ Don't:
// Multiple requests
const orders = await getOrders();
for (const order of orders) {
order.customer = await getCustomer(order.customer_id);
}✅ Do:
{
"op": "find",
"object": "orders",
"args": {
"expand": {
"customer": {
"fields": ["name", "email"]
}
}
}
}Next Steps
- Query Language Specification - Deep dive into filter syntax
- Actions Guide - Building custom RPC operations
- Server Integration - Deploying ObjectQL APIs
- Authentication Guide - Securing your APIs
- GraphQL API (Planned)
Support
- GitHub Issues: objectql/objectql/issues
- Documentation: objectql.org
- Community: Discord
Last Updated: January 2024
API Version: 1.0.0