Skip to content

Commit ea34cdf

Browse files
Copilothotlong
andcommitted
Add ObjectQL data source adapter package
Co-authored-by: hotlong <50353452+hotlong@users.noreply.github.com>
1 parent e59fb77 commit ea34cdf

8 files changed

Lines changed: 1510 additions & 0 deletions

File tree

packages/data-objectql/README.md

Lines changed: 389 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,389 @@
1+
# @object-ui/data-objectql
2+
3+
ObjectQL Data Source Adapter for Object UI - Seamlessly connect your Object UI components with ObjectQL API backends.
4+
5+
## Features
6+
7+
-**Universal DataSource Interface** - Implements the standard Object UI data source protocol
8+
-**Full TypeScript Support** - Complete type definitions and IntelliSense
9+
-**React Hooks** - Easy-to-use hooks for data fetching and mutations
10+
-**Automatic Query Conversion** - Converts universal query params to ObjectQL format
11+
-**Error Handling** - Robust error handling with typed error responses
12+
-**Authentication** - Built-in support for token-based authentication
13+
-**Multi-tenant** - Space ID support for multi-tenant environments
14+
15+
## Installation
16+
17+
```bash
18+
# Using npm
19+
npm install @object-ui/data-objectql
20+
21+
# Using yarn
22+
yarn add @object-ui/data-objectql
23+
24+
# Using pnpm
25+
pnpm add @object-ui/data-objectql
26+
```
27+
28+
## Quick Start
29+
30+
### Basic Usage
31+
32+
```typescript
33+
import { ObjectQLDataSource } from '@object-ui/data-objectql';
34+
35+
// Create a data source instance
36+
const dataSource = new ObjectQLDataSource({
37+
baseUrl: 'https://api.example.com',
38+
token: 'your-auth-token'
39+
});
40+
41+
// Fetch data
42+
const result = await dataSource.find('contacts', {
43+
$filter: { status: 'active' },
44+
$orderby: { created: 'desc' },
45+
$top: 10
46+
});
47+
48+
console.log(result.data); // Array of contacts
49+
```
50+
51+
### With React Components
52+
53+
```tsx
54+
import { SchemaRenderer } from '@object-ui/react';
55+
import { ObjectQLDataSource } from '@object-ui/data-objectql';
56+
57+
const dataSource = new ObjectQLDataSource({
58+
baseUrl: 'https://api.example.com',
59+
token: authToken
60+
});
61+
62+
const schema = {
63+
type: 'data-table',
64+
api: 'contacts',
65+
columns: [
66+
{ name: 'name', label: 'Name' },
67+
{ name: 'email', label: 'Email' },
68+
{ name: 'status', label: 'Status' }
69+
]
70+
};
71+
72+
function App() {
73+
return <SchemaRenderer schema={schema} dataSource={dataSource} />;
74+
}
75+
```
76+
77+
### Using React Hooks
78+
79+
```tsx
80+
import { useObjectQL, useObjectQLQuery, useObjectQLMutation } from '@object-ui/data-objectql';
81+
82+
function ContactList() {
83+
// Create data source
84+
const dataSource = useObjectQL({
85+
config: {
86+
baseUrl: 'https://api.example.com',
87+
token: authToken
88+
}
89+
});
90+
91+
// Query data
92+
const { data, loading, error, refetch } = useObjectQLQuery(
93+
dataSource,
94+
'contacts',
95+
{
96+
$filter: { status: 'active' },
97+
$orderby: { created: 'desc' },
98+
$top: 20
99+
}
100+
);
101+
102+
// Mutations
103+
const { create, update, remove } = useObjectQLMutation(
104+
dataSource,
105+
'contacts'
106+
);
107+
108+
const handleCreate = async () => {
109+
await create({
110+
name: 'John Doe',
111+
email: 'john@example.com'
112+
});
113+
refetch(); // Refresh the list
114+
};
115+
116+
if (loading) return <div>Loading...</div>;
117+
if (error) return <div>Error: {error.message}</div>;
118+
119+
return (
120+
<div>
121+
<button onClick={handleCreate}>Add Contact</button>
122+
<ul>
123+
{data?.map(contact => (
124+
<li key={contact._id}>{contact.name}</li>
125+
))}
126+
</ul>
127+
</div>
128+
);
129+
}
130+
```
131+
132+
## API Reference
133+
134+
### ObjectQLDataSource
135+
136+
#### Constructor
137+
138+
```typescript
139+
new ObjectQLDataSource(config: ObjectQLConfig)
140+
```
141+
142+
#### Configuration Options
143+
144+
```typescript
145+
interface ObjectQLConfig {
146+
baseUrl: string; // ObjectQL API base URL
147+
version?: string; // API version (default: 'v1')
148+
token?: string; // Authentication token
149+
spaceId?: string; // Space ID for multi-tenant
150+
headers?: Record<string, string>; // Additional headers
151+
timeout?: number; // Request timeout (default: 30000ms)
152+
withCredentials?: boolean; // Include credentials (default: true)
153+
}
154+
```
155+
156+
#### Methods
157+
158+
##### find(resource, params)
159+
160+
Fetch multiple records.
161+
162+
```typescript
163+
await dataSource.find('contacts', {
164+
$select: ['name', 'email', 'account.name'],
165+
$filter: { status: 'active' },
166+
$orderby: { created: 'desc' },
167+
$skip: 0,
168+
$top: 10,
169+
$count: true
170+
});
171+
```
172+
173+
##### findOne(resource, id, params)
174+
175+
Fetch a single record by ID.
176+
177+
```typescript
178+
const contact = await dataSource.findOne('contacts', '123', {
179+
$select: ['name', 'email', 'phone']
180+
});
181+
```
182+
183+
##### create(resource, data)
184+
185+
Create a new record.
186+
187+
```typescript
188+
const newContact = await dataSource.create('contacts', {
189+
name: 'John Doe',
190+
email: 'john@example.com',
191+
status: 'active'
192+
});
193+
```
194+
195+
##### update(resource, id, data)
196+
197+
Update an existing record.
198+
199+
```typescript
200+
const updated = await dataSource.update('contacts', '123', {
201+
status: 'inactive'
202+
});
203+
```
204+
205+
##### delete(resource, id)
206+
207+
Delete a record.
208+
209+
```typescript
210+
await dataSource.delete('contacts', '123');
211+
```
212+
213+
##### bulk(resource, operation, data)
214+
215+
Execute bulk operations.
216+
217+
```typescript
218+
const results = await dataSource.bulk('contacts', 'create', [
219+
{ name: 'Contact 1', email: 'contact1@example.com' },
220+
{ name: 'Contact 2', email: 'contact2@example.com' }
221+
]);
222+
```
223+
224+
### React Hooks
225+
226+
#### useObjectQL(options)
227+
228+
Create and manage an ObjectQL data source instance.
229+
230+
```typescript
231+
const dataSource = useObjectQL({
232+
config: {
233+
baseUrl: 'https://api.example.com',
234+
token: authToken
235+
}
236+
});
237+
```
238+
239+
#### useObjectQLQuery(dataSource, resource, options)
240+
241+
Fetch data with automatic loading and error states.
242+
243+
```typescript
244+
const { data, loading, error, refetch, result } = useObjectQLQuery(
245+
dataSource,
246+
'contacts',
247+
{
248+
$filter: { status: 'active' },
249+
enabled: true, // Auto-fetch on mount (default: true)
250+
refetchInterval: 5000, // Refetch every 5 seconds
251+
onSuccess: (data) => console.log('Data loaded:', data),
252+
onError: (error) => console.error('Error:', error)
253+
}
254+
);
255+
```
256+
257+
#### useObjectQLMutation(dataSource, resource, options)
258+
259+
Perform create, update, delete operations.
260+
261+
```typescript
262+
const { create, update, remove, loading, error } = useObjectQLMutation(
263+
dataSource,
264+
'contacts',
265+
{
266+
onSuccess: (data) => console.log('Success:', data),
267+
onError: (error) => console.error('Error:', error)
268+
}
269+
);
270+
271+
// Use the mutation functions
272+
await create({ name: 'New Contact' });
273+
await update('123', { name: 'Updated Name' });
274+
await remove('123');
275+
```
276+
277+
## Query Parameter Mapping
278+
279+
Object UI uses universal query parameters that are automatically converted to ObjectQL format:
280+
281+
| Universal Param | ObjectQL Param | Example |
282+
|----------------|----------------|---------|
283+
| `$select` | `fields` | `['name', 'email']` |
284+
| `$filter` | `filters` | `{ status: 'active' }` |
285+
| `$orderby` | `sort` | `{ created: -1 }` |
286+
| `$skip` | `skip` | `0` |
287+
| `$top` | `limit` | `10` |
288+
| `$count` | `count` | `true` |
289+
290+
## Advanced Usage
291+
292+
### Custom Headers
293+
294+
```typescript
295+
const dataSource = new ObjectQLDataSource({
296+
baseUrl: 'https://api.example.com',
297+
token: authToken,
298+
headers: {
299+
'X-Custom-Header': 'value',
300+
'X-Tenant-ID': 'tenant123'
301+
}
302+
});
303+
```
304+
305+
### Multi-tenant Support
306+
307+
```typescript
308+
const dataSource = new ObjectQLDataSource({
309+
baseUrl: 'https://api.example.com',
310+
token: authToken,
311+
spaceId: 'workspace123' // Automatically added to requests
312+
});
313+
```
314+
315+
### Complex Filters
316+
317+
```typescript
318+
const result = await dataSource.find('contacts', {
319+
$filter: {
320+
name: { $regex: '^John' },
321+
age: { $gte: 18, $lte: 65 },
322+
status: { $in: ['active', 'pending'] },
323+
'account.type': 'enterprise'
324+
}
325+
});
326+
```
327+
328+
### Field Selection with Relations
329+
330+
```typescript
331+
const result = await dataSource.find('contacts', {
332+
$select: [
333+
'name',
334+
'email',
335+
'account.name', // Related object field
336+
'account.industry', // Related object field
337+
'tasks.name', // Related list field
338+
'tasks.status' // Related list field
339+
]
340+
});
341+
```
342+
343+
## Error Handling
344+
345+
```typescript
346+
import type { APIError } from '@object-ui/types/data';
347+
348+
try {
349+
const result = await dataSource.find('contacts', params);
350+
} catch (err) {
351+
const error = err as APIError;
352+
console.error('Error:', error.message);
353+
console.error('Status:', error.status);
354+
console.error('Code:', error.code);
355+
console.error('Validation errors:', error.errors);
356+
}
357+
```
358+
359+
## TypeScript Support
360+
361+
Full TypeScript support with generics:
362+
363+
```typescript
364+
interface Contact {
365+
_id: string;
366+
name: string;
367+
email: string;
368+
status: 'active' | 'inactive';
369+
created: Date;
370+
}
371+
372+
const dataSource = new ObjectQLDataSource<Contact>({
373+
baseUrl: 'https://api.example.com'
374+
});
375+
376+
// Fully typed results
377+
const result = await dataSource.find('contacts');
378+
const contact: Contact = result.data[0]; // Typed!
379+
```
380+
381+
## License
382+
383+
MIT
384+
385+
## Links
386+
387+
- [Object UI Documentation](https://www.objectui.org)
388+
- [GitHub Repository](https://github.com/objectstack-ai/objectui)
389+
- [ObjectQL Documentation](https://www.objectql.com)

0 commit comments

Comments
 (0)