@rebasepro/cli 0.4.0 → 0.6.0
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/README.md +20 -28
- package/dist/commands/init.d.ts +3 -0
- package/dist/commands/skills.d.ts +1 -0
- package/dist/index.cjs +1843 -1414
- package/dist/index.cjs.map +1 -1
- package/dist/index.es.js +1620 -1257
- package/dist/index.es.js.map +1 -1
- package/dist/utils/package-manager.d.ts +2 -0
- package/package.json +15 -13
- package/skills/rebase-api/SKILL.md +662 -0
- package/skills/rebase-api/references/.gitkeep +3 -0
- package/skills/rebase-auth/SKILL.md +1143 -0
- package/skills/rebase-auth/references/.gitkeep +3 -0
- package/skills/rebase-backend-postgres/SKILL.md +633 -0
- package/skills/rebase-backend-postgres/references/.gitkeep +3 -0
- package/skills/rebase-basics/SKILL.md +749 -0
- package/skills/rebase-basics/references/.gitkeep +3 -0
- package/skills/rebase-collections/SKILL.md +1328 -0
- package/skills/rebase-collections/references/.gitkeep +3 -0
- package/skills/rebase-cron-jobs/SKILL.md +699 -0
- package/skills/rebase-cron-jobs/references/.gitkeep +1 -0
- package/skills/rebase-custom-functions/SKILL.md +233 -0
- package/skills/rebase-deployment/SKILL.md +583 -0
- package/skills/rebase-deployment/references/.gitkeep +3 -0
- package/skills/rebase-design-language/SKILL.md +664 -0
- package/skills/rebase-email/SKILL.md +701 -0
- package/skills/rebase-email/references/.gitkeep +1 -0
- package/skills/rebase-entity-history/SKILL.md +485 -0
- package/skills/rebase-entity-history/references/.gitkeep +1 -0
- package/skills/rebase-local-env-setup/SKILL.md +189 -0
- package/skills/rebase-local-env-setup/references/.gitkeep +3 -0
- package/skills/rebase-realtime/SKILL.md +755 -0
- package/skills/rebase-realtime/references/.gitkeep +3 -0
- package/skills/rebase-sdk/SKILL.md +594 -0
- package/skills/rebase-sdk/references/.gitkeep +0 -0
- package/skills/rebase-storage/SKILL.md +765 -0
- package/skills/rebase-storage/references/.gitkeep +3 -0
- package/skills/rebase-studio/SKILL.md +746 -0
- package/skills/rebase-studio/references/.gitkeep +3 -0
- package/skills/rebase-ui-components/SKILL.md +1411 -0
- package/skills/rebase-ui-components/references/.gitkeep +3 -0
- package/skills/rebase-webhooks/SKILL.md +623 -0
- package/skills/rebase-webhooks/references/.gitkeep +1 -0
- package/templates/template/AGENTS.md +2 -0
- package/templates/template/CLAUDE.md +2 -0
- package/templates/template/ai-instructions.md +6 -3
- package/templates/template/backend/drizzle.config.ts +8 -5
- package/templates/template/backend/package.json +1 -1
- package/templates/template/backend/src/env.ts +1 -1
- package/templates/template/backend/src/index.ts +9 -6
- package/templates/template/config/collections/posts.ts +1 -1
- package/templates/template/config/collections/presets/blank/index.ts +3 -0
- package/templates/template/config/collections/presets/ecommerce/categories.ts +38 -0
- package/templates/template/config/collections/presets/ecommerce/index.ts +6 -0
- package/templates/template/config/collections/presets/ecommerce/orders.ts +64 -0
- package/templates/template/config/collections/presets/ecommerce/products.ts +62 -0
- package/templates/template/config/collections/users.ts +7 -10
- package/templates/template/docker-compose.yml +1 -1
- package/templates/template/frontend/package.json +2 -2
- package/templates/template/frontend/src/App.tsx +1 -7
- package/templates/template/frontend/vite.config.ts +0 -1
- package/templates/template/package.json +7 -0
- package/dist/commands/cli.test.d.ts +0 -1
- package/dist/commands/dev.test.d.ts +0 -1
- package/dist/commands/init.test.d.ts +0 -1
- package/dist/utils/package-manager.test.d.ts +0 -1
- package/dist/utils/project.test.d.ts +0 -1
|
@@ -0,0 +1,594 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: rebase-sdk
|
|
3
|
+
description: Guide for using the Rebase generated TypeScript SDK and client library. Use this skill when the user needs to interact with a Rebase backend from client-side or server-side code, including CRUD operations, filtering, authentication, realtime subscriptions, file storage, custom functions, or admin operations.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Rebase SDK
|
|
7
|
+
|
|
8
|
+
The Rebase SDK consists of two packages that work together to provide end-to-end type-safe access to a Rebase backend:
|
|
9
|
+
|
|
10
|
+
- **`@rebasepro/client`** — The runtime client with CRUD, auth, realtime, storage, and more
|
|
11
|
+
- **SDK Generator** — Generates TypeScript types from your collection definitions (via CLI or MCP tool)
|
|
12
|
+
|
|
13
|
+
The generated types are passed as a generic parameter to the client, providing full type safety across all operations.
|
|
14
|
+
|
|
15
|
+
> **IMPORTANT FOR AGENTS:** Always generate the SDK types first before writing client code. Use the `rebase_generate_sdk` MCP tool or `npx rebase generate-sdk` CLI command. The generated types live at `./generated/sdk/database.types.ts` relative to the project root.
|
|
16
|
+
|
|
17
|
+
## SDK Generation
|
|
18
|
+
|
|
19
|
+
### How to Generate
|
|
20
|
+
|
|
21
|
+
**Via MCP tool (preferred):**
|
|
22
|
+
|
|
23
|
+
Use the `rebase_generate_sdk` MCP tool — no parameters required. It reads collections from `./config/collections` and writes the output to `./generated/sdk/`.
|
|
24
|
+
|
|
25
|
+
**Via CLI:**
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
npx rebase generate-sdk
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
### What Gets Generated
|
|
32
|
+
|
|
33
|
+
A single `database.types.ts` file containing:
|
|
34
|
+
|
|
35
|
+
| Export | Description |
|
|
36
|
+
|--------|-------------|
|
|
37
|
+
| `Database` | Interface with a key per collection, each containing `Row`, `Insert`, and `Update` sub-types |
|
|
38
|
+
| `CollectionName` | Union type of all collection slug strings |
|
|
39
|
+
| `CollectionsDictionary` | Runtime constant mapping camelCase names to snake_case slugs |
|
|
40
|
+
|
|
41
|
+
**Sub-type semantics:**
|
|
42
|
+
|
|
43
|
+
| Sub-type | Purpose | ID fields | Required fields |
|
|
44
|
+
|----------|---------|-----------|-----------------|
|
|
45
|
+
| `Row` | Read type — what you get back from queries | Required | All present |
|
|
46
|
+
| `Insert` | Create type — for new records | Optional (auto-generated) | Required fields stay required |
|
|
47
|
+
| `Update` | Partial update type | N/A | All optional |
|
|
48
|
+
|
|
49
|
+
### Property Type Mapping
|
|
50
|
+
|
|
51
|
+
| Rebase Type | TypeScript Type |
|
|
52
|
+
|-------------|-----------------|
|
|
53
|
+
| `string` | `string` (or string literal union if `enum` defined) |
|
|
54
|
+
| `number` | `number` (or number literal union if `enum` defined) |
|
|
55
|
+
| `boolean` | `boolean` |
|
|
56
|
+
| `date` | `string` (ISO 8601) |
|
|
57
|
+
| `geopoint` | `{ latitude: number; longitude: number }` |
|
|
58
|
+
| `reference` | `string \| number` |
|
|
59
|
+
| `map` | Nested `{ ... }` or `Record<string, any>` |
|
|
60
|
+
| `array` | `Array<T>` or `Array<any>` |
|
|
61
|
+
| `vector` | `number[]` |
|
|
62
|
+
| `binary` | `string` |
|
|
63
|
+
|
|
64
|
+
Relations are typed as `{ id: string; path: string; __type: "relation"; data?: RelatedRow }`.
|
|
65
|
+
|
|
66
|
+
## Client Initialization
|
|
67
|
+
|
|
68
|
+
```typescript
|
|
69
|
+
import { createRebaseClient } from '@rebasepro/client';
|
|
70
|
+
import type { Database } from './generated/sdk/database.types';
|
|
71
|
+
|
|
72
|
+
const rebase = createRebaseClient<Database>({
|
|
73
|
+
baseUrl: 'http://localhost:3001',
|
|
74
|
+
});
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### Typed SDK Return Type
|
|
78
|
+
|
|
79
|
+
The `createRebaseClient<DB>()` factory returns a reconciled client type of `CreateRebaseClientResult<DB>` (which extends the base `RebaseClient` from `@rebasepro/types`).
|
|
80
|
+
|
|
81
|
+
Passing your generated `Database` type maps camelCase collection names (e.g. `posts`) to their database schemas (`Row`, `Insert`, and `Update` types), providing autocomplete and type safety across the entire `data` layer.
|
|
82
|
+
|
|
83
|
+
### Configuration Options
|
|
84
|
+
|
|
85
|
+
| Option | Type | Default | Description |
|
|
86
|
+
|--------|------|---------|-------------|
|
|
87
|
+
| `baseUrl` | `string` | — | Backend URL (required) |
|
|
88
|
+
| `token` | `string` | — | Static JWT token (e.g., service key for server-side scripts) |
|
|
89
|
+
| `apiPath` | `string` | `'/api'` | API path prefix |
|
|
90
|
+
| `websocketUrl` | `string` | Auto-derived from `baseUrl` | WebSocket URL for realtime |
|
|
91
|
+
| `fetch` | `typeof fetch` | Global `fetch` | Custom fetch implementation |
|
|
92
|
+
| `onUnauthorized` | `() => void` | — | Handler called on 401 responses |
|
|
93
|
+
|
|
94
|
+
### Server-Side Usage
|
|
95
|
+
|
|
96
|
+
For server-side scripts or service-to-service calls, pass a service key as the `token`:
|
|
97
|
+
|
|
98
|
+
```typescript
|
|
99
|
+
const rebase = createRebaseClient<Database>({
|
|
100
|
+
baseUrl: 'https://api.myapp.com',
|
|
101
|
+
token: process.env.REBASE_SERVICE_KEY,
|
|
102
|
+
});
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## CRUD Operations
|
|
106
|
+
|
|
107
|
+
> **IMPORTANT FOR AGENTS:** There are two access patterns for collections. Both are fully typed when using the `Database` generic. Prefer the property-style access for better readability.
|
|
108
|
+
|
|
109
|
+
### Access Patterns
|
|
110
|
+
|
|
111
|
+
```typescript
|
|
112
|
+
// 1. Property-style access (camelCase — auto-converted to snake_case slug)
|
|
113
|
+
const { data: posts } = await rebase.data.posts.find();
|
|
114
|
+
|
|
115
|
+
// 2. Collection method (explicit slug)
|
|
116
|
+
const { data: posts } = await rebase.data.collection('posts').find();
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### Available Operations
|
|
120
|
+
|
|
121
|
+
| Method | Signature | Returns | Description |
|
|
122
|
+
|--------|-----------|---------|-------------|
|
|
123
|
+
| `find` | `find(params?)` | `{ data: Entity[], meta }` | List documents with filtering/pagination |
|
|
124
|
+
| `findById` | `findById(id)` | `Entity \| undefined` | Get a single document by ID |
|
|
125
|
+
| `create` | `create(data, id?)` | `Entity` | Create a new document |
|
|
126
|
+
| `update` | `update(id, data)` | `Entity` | Update an existing document |
|
|
127
|
+
| `delete` | `delete(id)` | `void` | Delete a document |
|
|
128
|
+
| `count` | `count(params?)` | `number` | Count matching documents |
|
|
129
|
+
|
|
130
|
+
### Examples
|
|
131
|
+
|
|
132
|
+
```typescript
|
|
133
|
+
// Create
|
|
134
|
+
const post = await rebase.data.posts.create({
|
|
135
|
+
title: 'Hello World',
|
|
136
|
+
status: 'draft',
|
|
137
|
+
content: 'My first post',
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
// Read
|
|
141
|
+
const post = await rebase.data.posts.findById('post-123');
|
|
142
|
+
|
|
143
|
+
// Update
|
|
144
|
+
const updated = await rebase.data.posts.update('post-123', {
|
|
145
|
+
status: 'published',
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
// Delete
|
|
149
|
+
await rebase.data.posts.delete('post-123');
|
|
150
|
+
|
|
151
|
+
// Count
|
|
152
|
+
const total = await rebase.data.posts.count({
|
|
153
|
+
where: { status: 'eq.published' },
|
|
154
|
+
});
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
### Response Format
|
|
158
|
+
|
|
159
|
+
The `find()` method returns a response with data and pagination metadata:
|
|
160
|
+
|
|
161
|
+
```typescript
|
|
162
|
+
const response = await rebase.data.posts.find({ limit: 20, offset: 0 });
|
|
163
|
+
|
|
164
|
+
// response.data → Entity[]
|
|
165
|
+
// response.meta → { total: number, limit: number, offset: number, hasMore: boolean }
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
## Filtering, Sorting & Pagination
|
|
169
|
+
|
|
170
|
+
There are two approaches to querying: **FindParams objects** and the **fluent QueryBuilder**.
|
|
171
|
+
|
|
172
|
+
### Approach 1: FindParams Object
|
|
173
|
+
|
|
174
|
+
Pass a params object directly to `.find()`:
|
|
175
|
+
|
|
176
|
+
```typescript
|
|
177
|
+
const result = await rebase.data.posts.find({
|
|
178
|
+
limit: 20,
|
|
179
|
+
offset: 0,
|
|
180
|
+
orderBy: 'createdAt:desc',
|
|
181
|
+
searchString: 'search term',
|
|
182
|
+
include: ['tags', 'author'], // or ['*'] for all relations
|
|
183
|
+
where: {
|
|
184
|
+
status: 'eq.published',
|
|
185
|
+
price: 'gte.100',
|
|
186
|
+
category: 'in.(electronics,books)',
|
|
187
|
+
},
|
|
188
|
+
});
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
### Filter Operators (String Syntax)
|
|
192
|
+
|
|
193
|
+
| Operator | Description | Example |
|
|
194
|
+
|----------|-------------|---------|
|
|
195
|
+
| `eq` | Equal | `'eq.published'` |
|
|
196
|
+
| `neq` | Not equal | `'neq.draft'` |
|
|
197
|
+
| `gt` | Greater than | `'gt.100'` |
|
|
198
|
+
| `gte` | Greater than or equal | `'gte.100'` |
|
|
199
|
+
| `lt` | Less than | `'lt.50'` |
|
|
200
|
+
| `lte` | Less than or equal | `'lte.50'` |
|
|
201
|
+
| `in` | In list | `'in.(electronics,books)'` |
|
|
202
|
+
| `nin` | Not in list | `'nin.(archived,deleted)'` |
|
|
203
|
+
| `cs` | Array contains | `'cs.tag1'` |
|
|
204
|
+
| `csa` | Array contains any | `'csa.(tag1,tag2)'` |
|
|
205
|
+
|
|
206
|
+
### Approach 2: Fluent QueryBuilder
|
|
207
|
+
|
|
208
|
+
Chain methods for a more readable query API:
|
|
209
|
+
|
|
210
|
+
```typescript
|
|
211
|
+
const result = await rebase.data.posts
|
|
212
|
+
.where('status', '==', 'published')
|
|
213
|
+
.where('price', '>=', 100)
|
|
214
|
+
.orderBy('createdAt', 'desc')
|
|
215
|
+
.limit(20)
|
|
216
|
+
.offset(0)
|
|
217
|
+
.search('keyword')
|
|
218
|
+
.include('tags', 'author')
|
|
219
|
+
.find();
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
### Complex Logical Conditions
|
|
223
|
+
|
|
224
|
+
Use `or`, `and`, and `cond` helpers for complex filters:
|
|
225
|
+
|
|
226
|
+
```typescript
|
|
227
|
+
import { or, and, cond } from '@rebasepro/client';
|
|
228
|
+
|
|
229
|
+
const result = await rebase.data.posts
|
|
230
|
+
.where(or(
|
|
231
|
+
cond('status', '==', 'published'),
|
|
232
|
+
and(
|
|
233
|
+
cond('status', '==', 'draft'),
|
|
234
|
+
cond('authorId', '==', currentUserId)
|
|
235
|
+
)
|
|
236
|
+
))
|
|
237
|
+
.find();
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
### QueryBuilder Methods
|
|
241
|
+
|
|
242
|
+
| Method | Description |
|
|
243
|
+
|--------|-------------|
|
|
244
|
+
| `.where(field, op, value)` | Add a filter condition |
|
|
245
|
+
| `.where(logicalCondition)` | Add complex `or`/`and`/`cond` filter |
|
|
246
|
+
| `.orderBy(field, direction?)` | Sort results (`'asc'` or `'desc'`) |
|
|
247
|
+
| `.limit(n)` | Limit number of results |
|
|
248
|
+
| `.offset(n)` | Skip first `n` results |
|
|
249
|
+
| `.search(term)` | Full-text search |
|
|
250
|
+
| `.include(...relations)` | Include related entities |
|
|
251
|
+
| `.find()` | Execute the query |
|
|
252
|
+
| `.listen(callback, errorCallback?)` | Subscribe to realtime updates |
|
|
253
|
+
|
|
254
|
+
## Authentication
|
|
255
|
+
|
|
256
|
+
### Email / Password
|
|
257
|
+
|
|
258
|
+
```typescript
|
|
259
|
+
// Sign up
|
|
260
|
+
await rebase.auth.signUp('user@example.com', 'password', 'Display Name');
|
|
261
|
+
|
|
262
|
+
// Sign in
|
|
263
|
+
await rebase.auth.signInWithEmail('user@example.com', 'password');
|
|
264
|
+
|
|
265
|
+
// Sign out
|
|
266
|
+
await rebase.auth.signOut();
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
### OAuth Providers
|
|
270
|
+
|
|
271
|
+
Twelve providers are supported with dedicated methods:
|
|
272
|
+
|
|
273
|
+
```typescript
|
|
274
|
+
// Google (ID token or authorization code flow)
|
|
275
|
+
await rebase.auth.signInWithGoogle({ idToken });
|
|
276
|
+
await rebase.auth.signInWithGoogle({ code, redirectUri });
|
|
277
|
+
|
|
278
|
+
// GitHub, Microsoft, Apple, Facebook, Twitter, Discord,
|
|
279
|
+
// GitLab, Bitbucket, Slack, Spotify, LinkedIn
|
|
280
|
+
await rebase.auth.signInWithGitHub(code, redirectUri);
|
|
281
|
+
await rebase.auth.signInWithMicrosoft(code, redirectUri);
|
|
282
|
+
await rebase.auth.signInWithApple(code, redirectUri);
|
|
283
|
+
// ... same pattern for all providers
|
|
284
|
+
|
|
285
|
+
// Generic OAuth (for custom providers)
|
|
286
|
+
await rebase.auth.signInWithOAuth('custom-provider', payload);
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
### Session Management
|
|
290
|
+
|
|
291
|
+
```typescript
|
|
292
|
+
// Get current session
|
|
293
|
+
const session = rebase.auth.getSession();
|
|
294
|
+
|
|
295
|
+
// Get current user
|
|
296
|
+
const user = await rebase.auth.getUser();
|
|
297
|
+
|
|
298
|
+
// Refresh session manually
|
|
299
|
+
await rebase.auth.refreshSession();
|
|
300
|
+
|
|
301
|
+
// Update user profile
|
|
302
|
+
await rebase.auth.updateUser({ displayName: 'New Name' });
|
|
303
|
+
|
|
304
|
+
// List active sessions
|
|
305
|
+
const sessions = await rebase.auth.getSessions();
|
|
306
|
+
|
|
307
|
+
// Revoke sessions
|
|
308
|
+
await rebase.auth.revokeSession(sessionId);
|
|
309
|
+
await rebase.auth.revokeAllSessions();
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
### Password Management
|
|
313
|
+
|
|
314
|
+
```typescript
|
|
315
|
+
// Forgot password flow
|
|
316
|
+
await rebase.auth.resetPasswordForEmail('user@example.com');
|
|
317
|
+
await rebase.auth.resetPassword(token, newPassword);
|
|
318
|
+
|
|
319
|
+
// Change password (while logged in)
|
|
320
|
+
await rebase.auth.changePassword(oldPassword, newPassword);
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
### Email Verification
|
|
324
|
+
|
|
325
|
+
```typescript
|
|
326
|
+
await rebase.auth.sendVerificationEmail();
|
|
327
|
+
await rebase.auth.verifyEmail(token);
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
### Auth State Listener
|
|
331
|
+
|
|
332
|
+
```typescript
|
|
333
|
+
const unsubscribe = rebase.auth.onAuthStateChange((event, session) => {
|
|
334
|
+
// event: 'SIGNED_IN' | 'SIGNED_OUT' | 'TOKEN_REFRESHED' | 'USER_UPDATED'
|
|
335
|
+
console.log('Auth event:', event);
|
|
336
|
+
});
|
|
337
|
+
|
|
338
|
+
// Clean up
|
|
339
|
+
unsubscribe();
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
**Session behavior:**
|
|
343
|
+
- Sessions are persisted to `localStorage` by default (configurable: `localStorage`, `memoryStorage`, `cookieStorage`)
|
|
344
|
+
- Tokens auto-refresh 120 seconds before expiry
|
|
345
|
+
- All requests automatically include the auth token
|
|
346
|
+
- On 401 responses, the client auto-refreshes the session and retries the request
|
|
347
|
+
|
|
348
|
+
## Realtime Subscriptions
|
|
349
|
+
|
|
350
|
+
### Listen to Collection Changes
|
|
351
|
+
|
|
352
|
+
```typescript
|
|
353
|
+
// Via CollectionClient
|
|
354
|
+
const unsubscribe = rebase.data.posts.listen(
|
|
355
|
+
{ where: { status: 'published' }, limit: 50 },
|
|
356
|
+
(response) => {
|
|
357
|
+
// response: FindResponse with updated data
|
|
358
|
+
console.log('Updated posts:', response.data);
|
|
359
|
+
},
|
|
360
|
+
(error) => {
|
|
361
|
+
console.error('Subscription error:', error);
|
|
362
|
+
}
|
|
363
|
+
);
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
### Listen to a Single Entity
|
|
367
|
+
|
|
368
|
+
```typescript
|
|
369
|
+
const unsubscribe = rebase.data.posts.listenById(
|
|
370
|
+
'post-123',
|
|
371
|
+
(entity) => {
|
|
372
|
+
// entity: Entity | undefined
|
|
373
|
+
console.log('Post updated:', entity);
|
|
374
|
+
},
|
|
375
|
+
(error) => {
|
|
376
|
+
console.error('Error:', error);
|
|
377
|
+
}
|
|
378
|
+
);
|
|
379
|
+
```
|
|
380
|
+
|
|
381
|
+
### Listen via QueryBuilder
|
|
382
|
+
|
|
383
|
+
```typescript
|
|
384
|
+
const unsubscribe = rebase.data.posts
|
|
385
|
+
.where('status', '==', 'published')
|
|
386
|
+
.orderBy('createdAt', 'desc')
|
|
387
|
+
.limit(20)
|
|
388
|
+
.listen((response) => {
|
|
389
|
+
console.log('Realtime updates:', response.data);
|
|
390
|
+
});
|
|
391
|
+
```
|
|
392
|
+
|
|
393
|
+
### WebSocket Events
|
|
394
|
+
|
|
395
|
+
```typescript
|
|
396
|
+
rebase.ws?.on('connect', () => { console.log('Connected'); });
|
|
397
|
+
rebase.ws?.on('disconnect', () => { console.log('Disconnected'); });
|
|
398
|
+
rebase.ws?.on('reconnect', () => { console.log('Reconnected'); });
|
|
399
|
+
rebase.ws?.on('error', (err) => { console.error('WS error:', err); });
|
|
400
|
+
|
|
401
|
+
// Disconnect manually
|
|
402
|
+
rebase.ws?.disconnect();
|
|
403
|
+
```
|
|
404
|
+
|
|
405
|
+
**Realtime features:**
|
|
406
|
+
- Auto-reconnect with exponential backoff (max 5 attempts)
|
|
407
|
+
- Subscription deduplication
|
|
408
|
+
- Client-side entity caching with structural merge
|
|
409
|
+
- Instant entity patches across subscriptions
|
|
410
|
+
- Auto-authentication and re-authentication on token refresh
|
|
411
|
+
|
|
412
|
+
## File Storage
|
|
413
|
+
|
|
414
|
+
```typescript
|
|
415
|
+
// Upload file
|
|
416
|
+
const result = await rebase.storage.putObject({
|
|
417
|
+
file: fileBlob, // File or Blob
|
|
418
|
+
key: 'path/to/file', // Optional custom key
|
|
419
|
+
bucket: 'my-bucket', // Optional bucket
|
|
420
|
+
metadata: { ... }, // Optional metadata
|
|
421
|
+
});
|
|
422
|
+
|
|
423
|
+
// Get signed download URL
|
|
424
|
+
const { url, metadata } = await rebase.storage.getSignedUrl('path/to/file', 'bucket');
|
|
425
|
+
|
|
426
|
+
// Download file
|
|
427
|
+
const file = await rebase.storage.getObject('path/to/file', 'bucket');
|
|
428
|
+
|
|
429
|
+
// Delete file
|
|
430
|
+
await rebase.storage.deleteObject('path/to/file', 'bucket');
|
|
431
|
+
|
|
432
|
+
// List files
|
|
433
|
+
const { items, nextPageToken } = await rebase.storage.listObjects('prefix/', {
|
|
434
|
+
bucket: 'my-bucket',
|
|
435
|
+
maxResults: 100,
|
|
436
|
+
pageToken: 'next-page-token',
|
|
437
|
+
});
|
|
438
|
+
```
|
|
439
|
+
|
|
440
|
+
## Custom Functions
|
|
441
|
+
|
|
442
|
+
Invoke custom server-side functions defined in the backend:
|
|
443
|
+
|
|
444
|
+
```typescript
|
|
445
|
+
// Basic invocation
|
|
446
|
+
const result = await rebase.functions.invoke<{ job: Job }>('extract-job', {
|
|
447
|
+
url: 'https://example.com/posting',
|
|
448
|
+
html: htmlContent,
|
|
449
|
+
});
|
|
450
|
+
|
|
451
|
+
// With additional options
|
|
452
|
+
await rebase.functions.invoke('my-function', payload, {
|
|
453
|
+
method: 'GET',
|
|
454
|
+
path: 'sub-path/123',
|
|
455
|
+
headers: { 'X-Custom': 'value' },
|
|
456
|
+
});
|
|
457
|
+
```
|
|
458
|
+
|
|
459
|
+
## Admin Operations
|
|
460
|
+
|
|
461
|
+
> **IMPORTANT FOR AGENTS:** Admin operations require a service key or admin-level JWT token. These should only be used in server-side scripts or admin panels — never expose them in client-side code.
|
|
462
|
+
|
|
463
|
+
```typescript
|
|
464
|
+
// List users
|
|
465
|
+
const users = await rebase.admin.listUsers();
|
|
466
|
+
|
|
467
|
+
// Paginated user listing
|
|
468
|
+
const result = await rebase.admin.listUsersPaginated({
|
|
469
|
+
search: 'john',
|
|
470
|
+
limit: 20,
|
|
471
|
+
offset: 0,
|
|
472
|
+
orderBy: 'createdAt',
|
|
473
|
+
orderDir: 'desc',
|
|
474
|
+
});
|
|
475
|
+
|
|
476
|
+
// CRUD operations
|
|
477
|
+
const user = await rebase.admin.getUser(userId);
|
|
478
|
+
const newUser = await rebase.admin.createUser({
|
|
479
|
+
email: 'new@example.com',
|
|
480
|
+
displayName: 'New User',
|
|
481
|
+
password: 'securePassword',
|
|
482
|
+
roles: ['editor'],
|
|
483
|
+
});
|
|
484
|
+
await rebase.admin.updateUser(userId, { displayName: 'Updated Name', roles: ['admin'] });
|
|
485
|
+
await rebase.admin.deleteUser(userId);
|
|
486
|
+
|
|
487
|
+
// Bootstrap (initial admin setup)
|
|
488
|
+
await rebase.admin.bootstrap();
|
|
489
|
+
```
|
|
490
|
+
|
|
491
|
+
## Cron Jobs
|
|
492
|
+
|
|
493
|
+
Manage scheduled background tasks from the client:
|
|
494
|
+
|
|
495
|
+
```typescript
|
|
496
|
+
// List all cron jobs
|
|
497
|
+
const jobs = await rebase.cron.listJobs();
|
|
498
|
+
|
|
499
|
+
// Get a specific job
|
|
500
|
+
const job = await rebase.cron.getJob(jobId);
|
|
501
|
+
|
|
502
|
+
// Trigger a job manually
|
|
503
|
+
await rebase.cron.triggerJob(jobId);
|
|
504
|
+
|
|
505
|
+
// Get job execution logs
|
|
506
|
+
const logs = await rebase.cron.getJobLogs(jobId, { limit: 50 });
|
|
507
|
+
|
|
508
|
+
// Enable/disable a job
|
|
509
|
+
await rebase.cron.toggleJob(jobId, true); // enable
|
|
510
|
+
await rebase.cron.toggleJob(jobId, false); // disable
|
|
511
|
+
```
|
|
512
|
+
|
|
513
|
+
## Generic Endpoint Call
|
|
514
|
+
|
|
515
|
+
Call any custom API endpoint directly:
|
|
516
|
+
|
|
517
|
+
```typescript
|
|
518
|
+
const result = await rebase.call<MyResponseType>('/my-endpoint', payload);
|
|
519
|
+
```
|
|
520
|
+
|
|
521
|
+
## React Integration Example
|
|
522
|
+
|
|
523
|
+
A common pattern for using the SDK in a React application:
|
|
524
|
+
|
|
525
|
+
```typescript
|
|
526
|
+
// src/client.ts
|
|
527
|
+
import { createRebaseClient } from '@rebasepro/client';
|
|
528
|
+
import type { Database } from './generated/sdk/database.types';
|
|
529
|
+
|
|
530
|
+
const API_URL = import.meta.env.VITE_API_URL || 'http://localhost:3001';
|
|
531
|
+
export const rebase = createRebaseClient<Database>({ baseUrl: API_URL });
|
|
532
|
+
```
|
|
533
|
+
|
|
534
|
+
```typescript
|
|
535
|
+
// src/hooks/useCollection.ts
|
|
536
|
+
import { useState, useEffect } from 'react';
|
|
537
|
+
import { rebase } from '../client';
|
|
538
|
+
|
|
539
|
+
export function useCollection<T extends keyof Database>(
|
|
540
|
+
slug: T,
|
|
541
|
+
options?: { limit?: number; where?: Record<string, string> }
|
|
542
|
+
) {
|
|
543
|
+
const [data, setData] = useState<Database[T]['Row'][]>([]);
|
|
544
|
+
const [loading, setLoading] = useState(true);
|
|
545
|
+
|
|
546
|
+
useEffect(() => {
|
|
547
|
+
const unsubscribe = rebase.data.collection(slug).listen(
|
|
548
|
+
{ limit: options?.limit, where: options?.where },
|
|
549
|
+
(response) => {
|
|
550
|
+
setData(response.data);
|
|
551
|
+
setLoading(false);
|
|
552
|
+
}
|
|
553
|
+
);
|
|
554
|
+
return unsubscribe;
|
|
555
|
+
}, [slug]);
|
|
556
|
+
|
|
557
|
+
return { data, loading };
|
|
558
|
+
}
|
|
559
|
+
```
|
|
560
|
+
|
|
561
|
+
```typescript
|
|
562
|
+
// src/hooks/useAuth.ts
|
|
563
|
+
import { useState, useEffect } from 'react';
|
|
564
|
+
import { rebase } from '../client';
|
|
565
|
+
|
|
566
|
+
export function useAuth() {
|
|
567
|
+
const [user, setUser] = useState(null);
|
|
568
|
+
|
|
569
|
+
useEffect(() => {
|
|
570
|
+
const unsubscribe = rebase.auth.onAuthStateChange((event, session) => {
|
|
571
|
+
setUser(session?.user ?? null);
|
|
572
|
+
});
|
|
573
|
+
return unsubscribe;
|
|
574
|
+
}, []);
|
|
575
|
+
|
|
576
|
+
return {
|
|
577
|
+
user,
|
|
578
|
+
signIn: (email: string, password: string) =>
|
|
579
|
+
rebase.auth.signInWithEmail(email, password),
|
|
580
|
+
signUp: (email: string, password: string, name: string) =>
|
|
581
|
+
rebase.auth.signUp(email, password, name),
|
|
582
|
+
signOut: () => rebase.auth.signOut(),
|
|
583
|
+
};
|
|
584
|
+
}
|
|
585
|
+
```
|
|
586
|
+
|
|
587
|
+
## References
|
|
588
|
+
|
|
589
|
+
- **Documentation:** [rebase.pro/docs](https://rebase.pro/docs)
|
|
590
|
+
- **GitHub:** [github.com/rebasepro/rebase](https://github.com/rebasepro/rebase)
|
|
591
|
+
- **Client package source:** `packages/client/src/`
|
|
592
|
+
- **SDK generator source:** `packages/sdk-generator/src/`
|
|
593
|
+
- **QueryBuilder source:** `packages/common/src/data/query_builder.ts`
|
|
594
|
+
- **SDK demo example:** `examples/sdk-demo/`
|
|
File without changes
|