@ereo/db-surrealdb 0.1.6
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 +1094 -0
- package/dist/adapter.d.ts +76 -0
- package/dist/adapter.d.ts.map +1 -0
- package/dist/config.d.ts +108 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/index.d.ts +58 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +393 -0
- package/dist/types.d.ts +162 -0
- package/dist/types.d.ts.map +1 -0
- package/package.json +57 -0
package/README.md
ADDED
|
@@ -0,0 +1,1094 @@
|
|
|
1
|
+
# @ereo/db-surrealdb
|
|
2
|
+
|
|
3
|
+
SurrealDB adapter for the EreoJS database abstraction layer. This package provides seamless integration between EreoJS applications and SurrealDB, a multi-model database that supports SQL-like queries, graph relationships, and real-time subscriptions.
|
|
4
|
+
|
|
5
|
+
## Table of Contents
|
|
6
|
+
|
|
7
|
+
- [Overview](#overview)
|
|
8
|
+
- [Installation](#installation)
|
|
9
|
+
- [Quick Start](#quick-start)
|
|
10
|
+
- [API Reference](#api-reference)
|
|
11
|
+
- [Adapter Factory](#adapter-factory)
|
|
12
|
+
- [Configuration Helpers](#configuration-helpers)
|
|
13
|
+
- [Authentication Helpers](#authentication-helpers)
|
|
14
|
+
- [URL Helpers](#url-helpers)
|
|
15
|
+
- [Preset Configurations](#preset-configurations)
|
|
16
|
+
- [Query Helpers](#query-helpers)
|
|
17
|
+
- [Validation](#validation)
|
|
18
|
+
- [Configuration Options](#configuration-options)
|
|
19
|
+
- [SurrealDB-Specific Features](#surrealdb-specific-features)
|
|
20
|
+
- [SurrealQL Queries](#surrealql-queries)
|
|
21
|
+
- [Record IDs](#record-ids)
|
|
22
|
+
- [Transactions](#transactions)
|
|
23
|
+
- [Connection Protocols](#connection-protocols)
|
|
24
|
+
- [Use Cases with Examples](#use-cases-with-examples)
|
|
25
|
+
- [Integration with Core db Package](#integration-with-core-db-package)
|
|
26
|
+
- [TypeScript Types Reference](#typescript-types-reference)
|
|
27
|
+
- [Troubleshooting / FAQ](#troubleshooting--faq)
|
|
28
|
+
|
|
29
|
+
## Overview
|
|
30
|
+
|
|
31
|
+
`@ereo/db-surrealdb` implements the `DatabaseAdapter` interface from `@ereo/db`, providing:
|
|
32
|
+
|
|
33
|
+
- Full SurrealDB client integration via the official `surrealdb` package
|
|
34
|
+
- Support for multiple authentication methods (root, namespace, database, record access, scope)
|
|
35
|
+
- HTTP, HTTPS, WebSocket, and secure WebSocket connection protocols
|
|
36
|
+
- Request-scoped query deduplication for optimal performance
|
|
37
|
+
- Transaction support with automatic commit/rollback
|
|
38
|
+
- Health check capabilities
|
|
39
|
+
- Edge runtime compatibility
|
|
40
|
+
|
|
41
|
+
## Installation
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
# Using bun
|
|
45
|
+
bun add @ereo/db-surrealdb surrealdb
|
|
46
|
+
|
|
47
|
+
# Using npm
|
|
48
|
+
npm install @ereo/db-surrealdb surrealdb
|
|
49
|
+
|
|
50
|
+
# Using pnpm
|
|
51
|
+
pnpm add @ereo/db-surrealdb surrealdb
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
**Peer Dependencies:**
|
|
55
|
+
- `surrealdb` (^1.0.0) - Required
|
|
56
|
+
|
|
57
|
+
## Quick Start
|
|
58
|
+
|
|
59
|
+
### Basic Setup
|
|
60
|
+
|
|
61
|
+
```typescript
|
|
62
|
+
// ereo.config.ts
|
|
63
|
+
import { defineConfig } from '@ereo/core';
|
|
64
|
+
import { createSurrealAdapter, defineSurrealConfig, createDatabasePlugin } from '@ereo/db-surrealdb';
|
|
65
|
+
|
|
66
|
+
const adapter = createSurrealAdapter(defineSurrealConfig({
|
|
67
|
+
url: 'http://localhost:8000',
|
|
68
|
+
namespace: 'test',
|
|
69
|
+
database: 'test',
|
|
70
|
+
auth: {
|
|
71
|
+
username: 'root',
|
|
72
|
+
password: 'root',
|
|
73
|
+
},
|
|
74
|
+
}));
|
|
75
|
+
|
|
76
|
+
export default defineConfig({
|
|
77
|
+
plugins: [createDatabasePlugin(adapter)],
|
|
78
|
+
});
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### Using in Routes
|
|
82
|
+
|
|
83
|
+
```typescript
|
|
84
|
+
// routes/users.ts
|
|
85
|
+
import { createLoader } from '@ereo/core';
|
|
86
|
+
import { useDb } from '@ereo/db-surrealdb';
|
|
87
|
+
|
|
88
|
+
export const loader = createLoader({
|
|
89
|
+
load: async ({ context }) => {
|
|
90
|
+
const db = useDb(context);
|
|
91
|
+
|
|
92
|
+
// Use SurrealQL
|
|
93
|
+
const result = await db.client.query('SELECT * FROM users WHERE active = true');
|
|
94
|
+
|
|
95
|
+
// Or use convenience methods
|
|
96
|
+
const users = await db.client.select('users');
|
|
97
|
+
|
|
98
|
+
return users;
|
|
99
|
+
},
|
|
100
|
+
});
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### Environment-Based Configuration
|
|
104
|
+
|
|
105
|
+
```typescript
|
|
106
|
+
import { createSurrealAdapter, envConfig } from '@ereo/db-surrealdb';
|
|
107
|
+
|
|
108
|
+
// Reads from environment variables:
|
|
109
|
+
// - SURREAL_URL (required)
|
|
110
|
+
// - SURREAL_NAMESPACE (required)
|
|
111
|
+
// - SURREAL_DATABASE (required)
|
|
112
|
+
// - SURREAL_USERNAME (optional)
|
|
113
|
+
// - SURREAL_PASSWORD (optional)
|
|
114
|
+
// - SURREAL_DEBUG (optional, 'true' to enable)
|
|
115
|
+
const adapter = createSurrealAdapter(envConfig());
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
## API Reference
|
|
119
|
+
|
|
120
|
+
### Adapter Factory
|
|
121
|
+
|
|
122
|
+
#### `createSurrealAdapter(config: SurrealDBConfig): DatabaseAdapter<SurrealClient>`
|
|
123
|
+
|
|
124
|
+
Creates a SurrealDB database adapter instance.
|
|
125
|
+
|
|
126
|
+
```typescript
|
|
127
|
+
import { createSurrealAdapter } from '@ereo/db-surrealdb';
|
|
128
|
+
|
|
129
|
+
const adapter = createSurrealAdapter({
|
|
130
|
+
url: 'http://localhost:8000',
|
|
131
|
+
namespace: 'test',
|
|
132
|
+
database: 'test',
|
|
133
|
+
auth: {
|
|
134
|
+
username: 'root',
|
|
135
|
+
password: 'root',
|
|
136
|
+
},
|
|
137
|
+
});
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
**Returns:** A `DatabaseAdapter<SurrealClient>` instance implementing:
|
|
141
|
+
- `name`: `'surrealdb'`
|
|
142
|
+
- `edgeCompatible`: `true`
|
|
143
|
+
- `getClient()`: Returns the SurrealDB client
|
|
144
|
+
- `getRequestClient(context)`: Returns a request-scoped client with deduplication
|
|
145
|
+
- `query(sql, params?)`: Execute a SELECT query
|
|
146
|
+
- `execute(sql, params?)`: Execute a mutation (INSERT/UPDATE/DELETE)
|
|
147
|
+
- `transaction(fn, options?)`: Run operations in a transaction
|
|
148
|
+
- `beginTransaction(options?)`: Start a manual transaction
|
|
149
|
+
- `healthCheck()`: Check database connectivity
|
|
150
|
+
- `disconnect()`: Close the connection
|
|
151
|
+
|
|
152
|
+
### Configuration Helpers
|
|
153
|
+
|
|
154
|
+
#### `defineSurrealConfig(config: SurrealDBConfig): SurrealDBConfig`
|
|
155
|
+
|
|
156
|
+
Type-safe configuration builder with sensible defaults.
|
|
157
|
+
|
|
158
|
+
```typescript
|
|
159
|
+
import { defineSurrealConfig } from '@ereo/db-surrealdb';
|
|
160
|
+
|
|
161
|
+
const config = defineSurrealConfig({
|
|
162
|
+
url: 'http://localhost:8000',
|
|
163
|
+
namespace: 'test',
|
|
164
|
+
database: 'test',
|
|
165
|
+
auth: {
|
|
166
|
+
username: 'root',
|
|
167
|
+
password: 'root',
|
|
168
|
+
},
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
// Defaults applied:
|
|
172
|
+
// - timeout: 30000
|
|
173
|
+
// - debug: false
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
### Authentication Helpers
|
|
177
|
+
|
|
178
|
+
#### `rootAuth(username: string, password: string): RootAuth`
|
|
179
|
+
|
|
180
|
+
Create root-level authentication credentials.
|
|
181
|
+
|
|
182
|
+
```typescript
|
|
183
|
+
import { rootAuth } from '@ereo/db-surrealdb';
|
|
184
|
+
|
|
185
|
+
const auth = rootAuth('root', 'surrealdb');
|
|
186
|
+
// => { username: 'root', password: 'surrealdb' }
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
#### `namespaceAuth(namespace: string, username: string, password: string): NamespaceAuth`
|
|
190
|
+
|
|
191
|
+
Create namespace-level authentication credentials.
|
|
192
|
+
|
|
193
|
+
```typescript
|
|
194
|
+
import { namespaceAuth } from '@ereo/db-surrealdb';
|
|
195
|
+
|
|
196
|
+
const auth = namespaceAuth('myns', 'admin', 'password');
|
|
197
|
+
// => { namespace: 'myns', username: 'admin', password: 'password' }
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
#### `databaseAuth(namespace: string, database: string, username: string, password: string): DatabaseAuth`
|
|
201
|
+
|
|
202
|
+
Create database-level authentication credentials.
|
|
203
|
+
|
|
204
|
+
```typescript
|
|
205
|
+
import { databaseAuth } from '@ereo/db-surrealdb';
|
|
206
|
+
|
|
207
|
+
const auth = databaseAuth('myns', 'mydb', 'admin', 'password');
|
|
208
|
+
// => { namespace: 'myns', database: 'mydb', username: 'admin', password: 'password' }
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
#### `recordAccessAuth(namespace: string, database: string, access: string, variables?: Record<string, unknown>): RecordAccessAuth`
|
|
212
|
+
|
|
213
|
+
Create record access authentication (SurrealDB 2.x+).
|
|
214
|
+
|
|
215
|
+
```typescript
|
|
216
|
+
import { recordAccessAuth } from '@ereo/db-surrealdb';
|
|
217
|
+
|
|
218
|
+
const auth = recordAccessAuth('myns', 'mydb', 'user', {
|
|
219
|
+
email: 'user@example.com',
|
|
220
|
+
password: 'secret',
|
|
221
|
+
});
|
|
222
|
+
// => { namespace: 'myns', database: 'mydb', access: 'user', variables: { email: '...', password: '...' } }
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
### URL Helpers
|
|
226
|
+
|
|
227
|
+
#### `buildSurrealUrl(host: string, port?: number, protocol?: ConnectionProtocol): string`
|
|
228
|
+
|
|
229
|
+
Build a SurrealDB connection URL.
|
|
230
|
+
|
|
231
|
+
```typescript
|
|
232
|
+
import { buildSurrealUrl } from '@ereo/db-surrealdb';
|
|
233
|
+
|
|
234
|
+
buildSurrealUrl('localhost', 8000, 'http');
|
|
235
|
+
// => 'http://localhost:8000'
|
|
236
|
+
|
|
237
|
+
buildSurrealUrl('cloud.surrealdb.com', 443, 'https');
|
|
238
|
+
// => 'https://cloud.surrealdb.com:443'
|
|
239
|
+
|
|
240
|
+
buildSurrealUrl('localhost', 8000, 'ws');
|
|
241
|
+
// => 'ws://localhost:8000'
|
|
242
|
+
|
|
243
|
+
// With defaults (port: 8000, protocol: 'http')
|
|
244
|
+
buildSurrealUrl('localhost');
|
|
245
|
+
// => 'http://localhost:8000'
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
#### `parseSurrealUrl(url: string): { protocol: string; host: string; port: number; path: string }`
|
|
249
|
+
|
|
250
|
+
Parse a SurrealDB connection URL into components.
|
|
251
|
+
|
|
252
|
+
```typescript
|
|
253
|
+
import { parseSurrealUrl } from '@ereo/db-surrealdb';
|
|
254
|
+
|
|
255
|
+
parseSurrealUrl('http://localhost:8000');
|
|
256
|
+
// => { protocol: 'http', host: 'localhost', port: 8000, path: '/' }
|
|
257
|
+
|
|
258
|
+
parseSurrealUrl('https://cloud.surrealdb.com');
|
|
259
|
+
// => { protocol: 'https', host: 'cloud.surrealdb.com', port: 443, path: '/' }
|
|
260
|
+
|
|
261
|
+
parseSurrealUrl('wss://cloud.surrealdb.com:8080/custom');
|
|
262
|
+
// => { protocol: 'wss', host: 'cloud.surrealdb.com', port: 8080, path: '/custom' }
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
### Preset Configurations
|
|
266
|
+
|
|
267
|
+
#### `localConfig(namespace: string, database: string, options?: Partial<SurrealDBConfig>): SurrealDBConfig`
|
|
268
|
+
|
|
269
|
+
Create a local development configuration.
|
|
270
|
+
|
|
271
|
+
```typescript
|
|
272
|
+
import { localConfig, rootAuth } from '@ereo/db-surrealdb';
|
|
273
|
+
|
|
274
|
+
const config = localConfig('test', 'test');
|
|
275
|
+
// => { url: 'http://127.0.0.1:8000', namespace: 'test', database: 'test', debug: true }
|
|
276
|
+
|
|
277
|
+
// With authentication
|
|
278
|
+
const configWithAuth = localConfig('test', 'test', {
|
|
279
|
+
auth: rootAuth('root', 'root'),
|
|
280
|
+
debug: false,
|
|
281
|
+
});
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
#### `cloudConfig(url: string, namespace: string, database: string, auth: SurrealAuth, options?: Partial<SurrealDBConfig>): SurrealDBConfig`
|
|
285
|
+
|
|
286
|
+
Create a cloud/production configuration.
|
|
287
|
+
|
|
288
|
+
```typescript
|
|
289
|
+
import { cloudConfig, rootAuth } from '@ereo/db-surrealdb';
|
|
290
|
+
|
|
291
|
+
const config = cloudConfig(
|
|
292
|
+
'https://cloud.surrealdb.com',
|
|
293
|
+
'production',
|
|
294
|
+
'mydb',
|
|
295
|
+
rootAuth('user', 'password')
|
|
296
|
+
);
|
|
297
|
+
// => { url: 'https://...', namespace: 'production', database: 'mydb', auth: {...}, debug: false }
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
#### `envConfig(env?: Record<string, string | undefined>): SurrealDBConfig`
|
|
301
|
+
|
|
302
|
+
Create configuration from environment variables.
|
|
303
|
+
|
|
304
|
+
```typescript
|
|
305
|
+
import { envConfig } from '@ereo/db-surrealdb';
|
|
306
|
+
|
|
307
|
+
// Uses process.env by default
|
|
308
|
+
const config = envConfig();
|
|
309
|
+
|
|
310
|
+
// Or provide custom env object (useful for testing)
|
|
311
|
+
const config = envConfig({
|
|
312
|
+
SURREAL_URL: 'http://localhost:8000',
|
|
313
|
+
SURREAL_NAMESPACE: 'test',
|
|
314
|
+
SURREAL_DATABASE: 'test',
|
|
315
|
+
SURREAL_USERNAME: 'root',
|
|
316
|
+
SURREAL_PASSWORD: 'root',
|
|
317
|
+
SURREAL_DEBUG: 'true',
|
|
318
|
+
});
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
**Expected Environment Variables:**
|
|
322
|
+
| Variable | Required | Description |
|
|
323
|
+
|----------|----------|-------------|
|
|
324
|
+
| `SURREAL_URL` | Yes | SurrealDB connection URL |
|
|
325
|
+
| `SURREAL_NAMESPACE` | Yes | Target namespace |
|
|
326
|
+
| `SURREAL_DATABASE` | Yes | Target database |
|
|
327
|
+
| `SURREAL_USERNAME` | No | Authentication username |
|
|
328
|
+
| `SURREAL_PASSWORD` | No | Authentication password |
|
|
329
|
+
| `SURREAL_DEBUG` | No | Enable debug logging (`'true'`) |
|
|
330
|
+
|
|
331
|
+
### Query Helpers
|
|
332
|
+
|
|
333
|
+
#### `select(table: string, options?: SelectOptions): string`
|
|
334
|
+
|
|
335
|
+
Create a SurrealQL SELECT query string.
|
|
336
|
+
|
|
337
|
+
```typescript
|
|
338
|
+
import { select } from '@ereo/db-surrealdb';
|
|
339
|
+
|
|
340
|
+
select('users');
|
|
341
|
+
// => 'SELECT * FROM users'
|
|
342
|
+
|
|
343
|
+
select('users', { where: 'active = true' });
|
|
344
|
+
// => 'SELECT * FROM users WHERE active = true'
|
|
345
|
+
|
|
346
|
+
select('users', { orderBy: 'name ASC' });
|
|
347
|
+
// => 'SELECT * FROM users ORDER BY name ASC'
|
|
348
|
+
|
|
349
|
+
select('users', { limit: 10 });
|
|
350
|
+
// => 'SELECT * FROM users LIMIT 10'
|
|
351
|
+
|
|
352
|
+
select('users', { start: 20 });
|
|
353
|
+
// => 'SELECT * FROM users START 20'
|
|
354
|
+
|
|
355
|
+
// Combine all options
|
|
356
|
+
select('users', {
|
|
357
|
+
where: 'active = true',
|
|
358
|
+
orderBy: 'created_at DESC',
|
|
359
|
+
limit: 10,
|
|
360
|
+
start: 0,
|
|
361
|
+
});
|
|
362
|
+
// => 'SELECT * FROM users WHERE active = true ORDER BY created_at DESC LIMIT 10 START 0'
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
#### `create(table: string, id?: string): string`
|
|
366
|
+
|
|
367
|
+
Create a SurrealQL CREATE query string.
|
|
368
|
+
|
|
369
|
+
```typescript
|
|
370
|
+
import { create } from '@ereo/db-surrealdb';
|
|
371
|
+
|
|
372
|
+
create('users');
|
|
373
|
+
// => 'CREATE users'
|
|
374
|
+
|
|
375
|
+
create('users', 'john');
|
|
376
|
+
// => 'CREATE users:john'
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
#### `update(table: string, id?: string): string`
|
|
380
|
+
|
|
381
|
+
Create a SurrealQL UPDATE query string.
|
|
382
|
+
|
|
383
|
+
```typescript
|
|
384
|
+
import { update } from '@ereo/db-surrealdb';
|
|
385
|
+
|
|
386
|
+
update('users');
|
|
387
|
+
// => 'UPDATE users'
|
|
388
|
+
|
|
389
|
+
update('users', 'john');
|
|
390
|
+
// => 'UPDATE users:john'
|
|
391
|
+
```
|
|
392
|
+
|
|
393
|
+
#### `deleteFrom(table: string, id?: string): string`
|
|
394
|
+
|
|
395
|
+
Create a SurrealQL DELETE query string.
|
|
396
|
+
|
|
397
|
+
```typescript
|
|
398
|
+
import { deleteFrom } from '@ereo/db-surrealdb';
|
|
399
|
+
|
|
400
|
+
deleteFrom('users');
|
|
401
|
+
// => 'DELETE users'
|
|
402
|
+
|
|
403
|
+
deleteFrom('users', 'john');
|
|
404
|
+
// => 'DELETE users:john'
|
|
405
|
+
```
|
|
406
|
+
|
|
407
|
+
### Validation
|
|
408
|
+
|
|
409
|
+
#### `validateConfig(config: SurrealDBConfig): void`
|
|
410
|
+
|
|
411
|
+
Validate a SurrealDB configuration. Throws an error if invalid.
|
|
412
|
+
|
|
413
|
+
```typescript
|
|
414
|
+
import { validateConfig } from '@ereo/db-surrealdb';
|
|
415
|
+
|
|
416
|
+
// Valid configurations
|
|
417
|
+
validateConfig({
|
|
418
|
+
url: 'http://localhost:8000',
|
|
419
|
+
namespace: 'test',
|
|
420
|
+
database: 'test',
|
|
421
|
+
}); // OK
|
|
422
|
+
|
|
423
|
+
validateConfig({
|
|
424
|
+
url: 'mem://',
|
|
425
|
+
namespace: 'test',
|
|
426
|
+
database: 'test',
|
|
427
|
+
}); // OK (in-memory)
|
|
428
|
+
|
|
429
|
+
validateConfig({
|
|
430
|
+
url: 'surrealkv://./data',
|
|
431
|
+
namespace: 'test',
|
|
432
|
+
database: 'test',
|
|
433
|
+
}); // OK (persistent)
|
|
434
|
+
|
|
435
|
+
// Invalid configurations throw errors
|
|
436
|
+
validateConfig({ url: '', namespace: 'test', database: 'test' });
|
|
437
|
+
// Throws: 'SurrealDB url is required'
|
|
438
|
+
|
|
439
|
+
validateConfig({ url: 'http://localhost:8000', namespace: '', database: 'test' });
|
|
440
|
+
// Throws: 'SurrealDB namespace is required'
|
|
441
|
+
|
|
442
|
+
validateConfig({ url: 'not-a-valid-url', namespace: 'test', database: 'test' });
|
|
443
|
+
// Throws: 'Invalid SurrealDB URL: not-a-valid-url'
|
|
444
|
+
```
|
|
445
|
+
|
|
446
|
+
## Configuration Options
|
|
447
|
+
|
|
448
|
+
The `SurrealDBConfig` interface extends `AdapterConfig` and includes:
|
|
449
|
+
|
|
450
|
+
| Option | Type | Required | Default | Description |
|
|
451
|
+
|--------|------|----------|---------|-------------|
|
|
452
|
+
| `url` | `string` | Yes | - | SurrealDB connection URL |
|
|
453
|
+
| `namespace` | `string` | Yes | - | Target namespace |
|
|
454
|
+
| `database` | `string` | Yes | - | Target database within the namespace |
|
|
455
|
+
| `auth` | `SurrealAuth` | No | - | Authentication credentials |
|
|
456
|
+
| `timeout` | `number` | No | `30000` | Connection timeout in milliseconds |
|
|
457
|
+
| `debug` | `boolean` | No | `false` | Enable debug logging |
|
|
458
|
+
|
|
459
|
+
**Supported URL Formats:**
|
|
460
|
+
- `http://127.0.0.1:8000` - HTTP connection
|
|
461
|
+
- `https://cloud.surrealdb.com` - HTTPS connection
|
|
462
|
+
- `ws://127.0.0.1:8000` - WebSocket connection
|
|
463
|
+
- `wss://cloud.surrealdb.com` - Secure WebSocket connection
|
|
464
|
+
- `mem://` - In-memory database (requires `@surrealdb/node`)
|
|
465
|
+
- `surrealkv://path/to/db` - Persistent storage (requires `@surrealdb/node`)
|
|
466
|
+
|
|
467
|
+
## SurrealDB-Specific Features
|
|
468
|
+
|
|
469
|
+
### SurrealQL Queries
|
|
470
|
+
|
|
471
|
+
Access the full power of SurrealQL through the client:
|
|
472
|
+
|
|
473
|
+
```typescript
|
|
474
|
+
import { useDb } from '@ereo/db-surrealdb';
|
|
475
|
+
|
|
476
|
+
export const loader = createLoader({
|
|
477
|
+
load: async ({ context }) => {
|
|
478
|
+
const db = useDb(context);
|
|
479
|
+
|
|
480
|
+
// Raw SurrealQL queries
|
|
481
|
+
const users = await db.client.query(`
|
|
482
|
+
SELECT *, ->follows->user AS following
|
|
483
|
+
FROM users
|
|
484
|
+
WHERE active = true
|
|
485
|
+
ORDER BY created_at DESC
|
|
486
|
+
LIMIT 10
|
|
487
|
+
`);
|
|
488
|
+
|
|
489
|
+
// Parameterized queries (parameters use $0, $1, etc.)
|
|
490
|
+
const result = await db.client.query(
|
|
491
|
+
'SELECT * FROM users WHERE email = $0',
|
|
492
|
+
['user@example.com']
|
|
493
|
+
);
|
|
494
|
+
|
|
495
|
+
return users;
|
|
496
|
+
},
|
|
497
|
+
});
|
|
498
|
+
```
|
|
499
|
+
|
|
500
|
+
### Record IDs
|
|
501
|
+
|
|
502
|
+
SurrealDB uses compound record IDs in the format `table:id`:
|
|
503
|
+
|
|
504
|
+
```typescript
|
|
505
|
+
import { useDb } from '@ereo/db-surrealdb';
|
|
506
|
+
|
|
507
|
+
export const loader = createLoader({
|
|
508
|
+
load: async ({ context }) => {
|
|
509
|
+
const db = useDb(context);
|
|
510
|
+
|
|
511
|
+
// Select a specific record
|
|
512
|
+
const user = await db.client.select('users:john');
|
|
513
|
+
|
|
514
|
+
// Create with specific ID
|
|
515
|
+
const newUser = await db.client.create('users:jane', {
|
|
516
|
+
name: 'Jane Doe',
|
|
517
|
+
email: 'jane@example.com',
|
|
518
|
+
});
|
|
519
|
+
|
|
520
|
+
// Update specific record
|
|
521
|
+
await db.client.update('users:john', {
|
|
522
|
+
name: 'John Smith',
|
|
523
|
+
});
|
|
524
|
+
|
|
525
|
+
// Delete specific record
|
|
526
|
+
await db.client.delete('users:john');
|
|
527
|
+
|
|
528
|
+
return user;
|
|
529
|
+
},
|
|
530
|
+
});
|
|
531
|
+
```
|
|
532
|
+
|
|
533
|
+
### Transactions
|
|
534
|
+
|
|
535
|
+
The adapter supports both automatic and manual transaction handling:
|
|
536
|
+
|
|
537
|
+
```typescript
|
|
538
|
+
import { useDb, withTransaction } from '@ereo/db-surrealdb';
|
|
539
|
+
|
|
540
|
+
// Automatic transaction (recommended)
|
|
541
|
+
export const action = createAction({
|
|
542
|
+
action: async ({ context }) => {
|
|
543
|
+
const db = useDb(context);
|
|
544
|
+
|
|
545
|
+
const result = await withTransaction(context, async (tx) => {
|
|
546
|
+
// All operations here are in a transaction
|
|
547
|
+
const user = await tx.create('users', { name: 'Alice' });
|
|
548
|
+
await tx.create('profiles', { user: user.id, bio: 'Hello!' });
|
|
549
|
+
return user;
|
|
550
|
+
});
|
|
551
|
+
|
|
552
|
+
return result;
|
|
553
|
+
},
|
|
554
|
+
});
|
|
555
|
+
|
|
556
|
+
// Manual transaction control
|
|
557
|
+
export const action = createAction({
|
|
558
|
+
action: async ({ context }) => {
|
|
559
|
+
const adapter = useAdapter(context);
|
|
560
|
+
const tx = await adapter.beginTransaction();
|
|
561
|
+
|
|
562
|
+
try {
|
|
563
|
+
await tx.client.query('CREATE users SET name = "Bob"');
|
|
564
|
+
await tx.client.query('CREATE profiles SET user = users:bob');
|
|
565
|
+
await tx.commit();
|
|
566
|
+
} catch (error) {
|
|
567
|
+
await tx.rollback();
|
|
568
|
+
throw error;
|
|
569
|
+
}
|
|
570
|
+
},
|
|
571
|
+
});
|
|
572
|
+
```
|
|
573
|
+
|
|
574
|
+
### Connection Protocols
|
|
575
|
+
|
|
576
|
+
The adapter automatically appends `/rpc` to HTTP/HTTPS URLs for compatibility with the SurrealDB RPC endpoint:
|
|
577
|
+
|
|
578
|
+
```typescript
|
|
579
|
+
// Input: 'http://localhost:8000'
|
|
580
|
+
// Connection URL: 'http://localhost:8000/rpc'
|
|
581
|
+
|
|
582
|
+
// Input: 'http://localhost:8000/'
|
|
583
|
+
// Connection URL: 'http://localhost:8000/rpc'
|
|
584
|
+
|
|
585
|
+
// Input: 'ws://localhost:8000'
|
|
586
|
+
// Connection URL: 'ws://localhost:8000' (unchanged for WebSocket)
|
|
587
|
+
```
|
|
588
|
+
|
|
589
|
+
## Use Cases with Examples
|
|
590
|
+
|
|
591
|
+
### Basic CRUD Operations
|
|
592
|
+
|
|
593
|
+
```typescript
|
|
594
|
+
import { createLoader, createAction } from '@ereo/core';
|
|
595
|
+
import { useDb } from '@ereo/db-surrealdb';
|
|
596
|
+
|
|
597
|
+
// Read all users
|
|
598
|
+
export const usersLoader = createLoader({
|
|
599
|
+
load: async ({ context }) => {
|
|
600
|
+
const db = useDb(context);
|
|
601
|
+
return db.client.select('users');
|
|
602
|
+
},
|
|
603
|
+
});
|
|
604
|
+
|
|
605
|
+
// Read single user
|
|
606
|
+
export const userLoader = createLoader({
|
|
607
|
+
load: async ({ context, params }) => {
|
|
608
|
+
const db = useDb(context);
|
|
609
|
+
return db.client.select(`users:${params.id}`);
|
|
610
|
+
},
|
|
611
|
+
});
|
|
612
|
+
|
|
613
|
+
// Create user
|
|
614
|
+
export const createUserAction = createAction({
|
|
615
|
+
action: async ({ context, request }) => {
|
|
616
|
+
const db = useDb(context);
|
|
617
|
+
const data = await request.json();
|
|
618
|
+
return db.client.create('users', data);
|
|
619
|
+
},
|
|
620
|
+
});
|
|
621
|
+
|
|
622
|
+
// Update user
|
|
623
|
+
export const updateUserAction = createAction({
|
|
624
|
+
action: async ({ context, params, request }) => {
|
|
625
|
+
const db = useDb(context);
|
|
626
|
+
const data = await request.json();
|
|
627
|
+
return db.client.merge(`users:${params.id}`, data);
|
|
628
|
+
},
|
|
629
|
+
});
|
|
630
|
+
|
|
631
|
+
// Delete user
|
|
632
|
+
export const deleteUserAction = createAction({
|
|
633
|
+
action: async ({ context, params }) => {
|
|
634
|
+
const db = useDb(context);
|
|
635
|
+
return db.client.delete(`users:${params.id}`);
|
|
636
|
+
},
|
|
637
|
+
});
|
|
638
|
+
```
|
|
639
|
+
|
|
640
|
+
### Graph Relationships
|
|
641
|
+
|
|
642
|
+
```typescript
|
|
643
|
+
import { useDb } from '@ereo/db-surrealdb';
|
|
644
|
+
|
|
645
|
+
export const loader = createLoader({
|
|
646
|
+
load: async ({ context, params }) => {
|
|
647
|
+
const db = useDb(context);
|
|
648
|
+
|
|
649
|
+
// Create a relationship
|
|
650
|
+
await db.client.query(`
|
|
651
|
+
RELATE users:${params.userId}->follows->users:${params.targetId}
|
|
652
|
+
SET created_at = time::now()
|
|
653
|
+
`);
|
|
654
|
+
|
|
655
|
+
// Query relationships
|
|
656
|
+
const following = await db.client.query(`
|
|
657
|
+
SELECT ->follows->user.* AS following
|
|
658
|
+
FROM users:${params.userId}
|
|
659
|
+
`);
|
|
660
|
+
|
|
661
|
+
const followers = await db.client.query(`
|
|
662
|
+
SELECT <-follows<-user.* AS followers
|
|
663
|
+
FROM users:${params.userId}
|
|
664
|
+
`);
|
|
665
|
+
|
|
666
|
+
// Mutual friends
|
|
667
|
+
const mutuals = await db.client.query(`
|
|
668
|
+
SELECT ->follows->user<-follows<-user AS mutuals
|
|
669
|
+
FROM users:${params.userId}
|
|
670
|
+
WHERE mutuals != users:${params.userId}
|
|
671
|
+
`);
|
|
672
|
+
|
|
673
|
+
return { following, followers, mutuals };
|
|
674
|
+
},
|
|
675
|
+
});
|
|
676
|
+
```
|
|
677
|
+
|
|
678
|
+
### Query Deduplication
|
|
679
|
+
|
|
680
|
+
The adapter automatically deduplicates identical queries within a request:
|
|
681
|
+
|
|
682
|
+
```typescript
|
|
683
|
+
import { useDb } from '@ereo/db-surrealdb';
|
|
684
|
+
|
|
685
|
+
export const loader = createLoader({
|
|
686
|
+
load: async ({ context }) => {
|
|
687
|
+
const db = useDb(context);
|
|
688
|
+
|
|
689
|
+
// These identical queries will only execute once
|
|
690
|
+
const [users1, users2, users3] = await Promise.all([
|
|
691
|
+
db.query('SELECT * FROM users'),
|
|
692
|
+
db.query('SELECT * FROM users'),
|
|
693
|
+
db.query('SELECT * FROM users'),
|
|
694
|
+
]);
|
|
695
|
+
|
|
696
|
+
// Check deduplication stats
|
|
697
|
+
const stats = db.getDedupStats();
|
|
698
|
+
console.log(stats);
|
|
699
|
+
// => { total: 3, deduplicated: 2, unique: 1, hitRate: 0.67 }
|
|
700
|
+
|
|
701
|
+
// Clear cache after mutations
|
|
702
|
+
await db.client.create('users', { name: 'New User' });
|
|
703
|
+
db.invalidate(['users']); // or db.clearDedup() to clear all
|
|
704
|
+
|
|
705
|
+
return users1;
|
|
706
|
+
},
|
|
707
|
+
});
|
|
708
|
+
```
|
|
709
|
+
|
|
710
|
+
### Health Checks
|
|
711
|
+
|
|
712
|
+
```typescript
|
|
713
|
+
import { createSurrealAdapter } from '@ereo/db-surrealdb';
|
|
714
|
+
|
|
715
|
+
const adapter = createSurrealAdapter(config);
|
|
716
|
+
|
|
717
|
+
// Check database health
|
|
718
|
+
const health = await adapter.healthCheck();
|
|
719
|
+
|
|
720
|
+
if (health.healthy) {
|
|
721
|
+
console.log(`Database is healthy (latency: ${health.latencyMs}ms)`);
|
|
722
|
+
console.log(`Connected to: ${health.metadata?.namespace}/${health.metadata?.database}`);
|
|
723
|
+
} else {
|
|
724
|
+
console.error(`Database unhealthy: ${health.error}`);
|
|
725
|
+
}
|
|
726
|
+
```
|
|
727
|
+
|
|
728
|
+
### Multi-Tenant Configuration
|
|
729
|
+
|
|
730
|
+
```typescript
|
|
731
|
+
import { createSurrealAdapter, defineSurrealConfig, databaseAuth } from '@ereo/db-surrealdb';
|
|
732
|
+
|
|
733
|
+
// Create tenant-specific adapter
|
|
734
|
+
function createTenantAdapter(tenantId: string) {
|
|
735
|
+
return createSurrealAdapter(defineSurrealConfig({
|
|
736
|
+
url: process.env.SURREAL_URL!,
|
|
737
|
+
namespace: 'production',
|
|
738
|
+
database: `tenant_${tenantId}`,
|
|
739
|
+
auth: databaseAuth(
|
|
740
|
+
'production',
|
|
741
|
+
`tenant_${tenantId}`,
|
|
742
|
+
process.env.TENANT_USER!,
|
|
743
|
+
process.env.TENANT_PASSWORD!
|
|
744
|
+
),
|
|
745
|
+
}));
|
|
746
|
+
}
|
|
747
|
+
```
|
|
748
|
+
|
|
749
|
+
## Integration with Core db Package
|
|
750
|
+
|
|
751
|
+
This package re-exports commonly used items from `@ereo/db` for convenience:
|
|
752
|
+
|
|
753
|
+
```typescript
|
|
754
|
+
import {
|
|
755
|
+
// Plugin factory
|
|
756
|
+
createDatabasePlugin,
|
|
757
|
+
|
|
758
|
+
// Context helpers
|
|
759
|
+
useDb,
|
|
760
|
+
useAdapter,
|
|
761
|
+
getDb,
|
|
762
|
+
|
|
763
|
+
// Transaction helper
|
|
764
|
+
withTransaction,
|
|
765
|
+
|
|
766
|
+
// Types
|
|
767
|
+
type DatabaseAdapter,
|
|
768
|
+
type RequestScopedClient,
|
|
769
|
+
type QueryResult,
|
|
770
|
+
type MutationResult,
|
|
771
|
+
type DedupResult,
|
|
772
|
+
type DedupStats,
|
|
773
|
+
type TransactionOptions,
|
|
774
|
+
} from '@ereo/db-surrealdb';
|
|
775
|
+
```
|
|
776
|
+
|
|
777
|
+
## TypeScript Types Reference
|
|
778
|
+
|
|
779
|
+
### Authentication Types
|
|
780
|
+
|
|
781
|
+
```typescript
|
|
782
|
+
// Root user authentication
|
|
783
|
+
interface RootAuth {
|
|
784
|
+
username: string;
|
|
785
|
+
password: string;
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
// Namespace-level authentication
|
|
789
|
+
interface NamespaceAuth {
|
|
790
|
+
namespace: string;
|
|
791
|
+
username: string;
|
|
792
|
+
password: string;
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
// Database-level authentication
|
|
796
|
+
interface DatabaseAuth {
|
|
797
|
+
namespace: string;
|
|
798
|
+
database: string;
|
|
799
|
+
username: string;
|
|
800
|
+
password: string;
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
// Record access authentication (SurrealDB 2.x+)
|
|
804
|
+
interface RecordAccessAuth {
|
|
805
|
+
namespace: string;
|
|
806
|
+
database: string;
|
|
807
|
+
access: string;
|
|
808
|
+
variables?: Record<string, unknown>;
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
// Scope-based authentication (SurrealDB 1.x)
|
|
812
|
+
interface ScopeAuth {
|
|
813
|
+
namespace: string;
|
|
814
|
+
database: string;
|
|
815
|
+
scope: string;
|
|
816
|
+
[key: string]: unknown;
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
// Union of all authentication types
|
|
820
|
+
type SurrealAuth = RootAuth | NamespaceAuth | DatabaseAuth | RecordAccessAuth | ScopeAuth;
|
|
821
|
+
```
|
|
822
|
+
|
|
823
|
+
### Configuration Types
|
|
824
|
+
|
|
825
|
+
```typescript
|
|
826
|
+
// Connection protocol
|
|
827
|
+
type ConnectionProtocol = 'http' | 'https' | 'ws' | 'wss';
|
|
828
|
+
|
|
829
|
+
// Engine type
|
|
830
|
+
type SurrealEngine = 'remote' | 'memory' | 'surrealkv';
|
|
831
|
+
|
|
832
|
+
// Main configuration
|
|
833
|
+
interface SurrealDBConfig extends AdapterConfig {
|
|
834
|
+
url: string;
|
|
835
|
+
namespace: string;
|
|
836
|
+
database: string;
|
|
837
|
+
auth?: SurrealAuth;
|
|
838
|
+
timeout?: number;
|
|
839
|
+
debug?: boolean;
|
|
840
|
+
}
|
|
841
|
+
```
|
|
842
|
+
|
|
843
|
+
### Query Result Types
|
|
844
|
+
|
|
845
|
+
```typescript
|
|
846
|
+
// SurrealDB query result
|
|
847
|
+
interface SurrealQueryResult<T = unknown> {
|
|
848
|
+
result: T;
|
|
849
|
+
status: 'OK' | 'ERR';
|
|
850
|
+
time: string;
|
|
851
|
+
}
|
|
852
|
+
|
|
853
|
+
// Raw RPC response
|
|
854
|
+
interface SurrealRawResponse<T = unknown> {
|
|
855
|
+
result: T;
|
|
856
|
+
status: string;
|
|
857
|
+
time: string;
|
|
858
|
+
}
|
|
859
|
+
```
|
|
860
|
+
|
|
861
|
+
### Record Types
|
|
862
|
+
|
|
863
|
+
```typescript
|
|
864
|
+
// SurrealDB Record ID (format: "table:id")
|
|
865
|
+
interface RecordId<T extends string = string> {
|
|
866
|
+
tb: T;
|
|
867
|
+
id: string | number | object;
|
|
868
|
+
}
|
|
869
|
+
|
|
870
|
+
// Base record with ID
|
|
871
|
+
interface SurrealRecord {
|
|
872
|
+
id: RecordId | string;
|
|
873
|
+
}
|
|
874
|
+
```
|
|
875
|
+
|
|
876
|
+
### Live Query Types
|
|
877
|
+
|
|
878
|
+
```typescript
|
|
879
|
+
// Live query action types
|
|
880
|
+
type LiveAction = 'CREATE' | 'UPDATE' | 'DELETE';
|
|
881
|
+
|
|
882
|
+
// Live query notification
|
|
883
|
+
interface LiveNotification<T = unknown> {
|
|
884
|
+
action: LiveAction;
|
|
885
|
+
result: T;
|
|
886
|
+
}
|
|
887
|
+
```
|
|
888
|
+
|
|
889
|
+
### Type Guards
|
|
890
|
+
|
|
891
|
+
```typescript
|
|
892
|
+
import {
|
|
893
|
+
isRootAuth,
|
|
894
|
+
isNamespaceAuth,
|
|
895
|
+
isDatabaseAuth,
|
|
896
|
+
isRecordAccessAuth,
|
|
897
|
+
isScopeAuth,
|
|
898
|
+
} from '@ereo/db-surrealdb';
|
|
899
|
+
|
|
900
|
+
// Example usage
|
|
901
|
+
function handleAuth(auth: SurrealAuth) {
|
|
902
|
+
if (isRootAuth(auth)) {
|
|
903
|
+
console.log('Root auth:', auth.username);
|
|
904
|
+
} else if (isNamespaceAuth(auth)) {
|
|
905
|
+
console.log('Namespace auth:', auth.namespace, auth.username);
|
|
906
|
+
} else if (isDatabaseAuth(auth)) {
|
|
907
|
+
console.log('Database auth:', auth.namespace, auth.database, auth.username);
|
|
908
|
+
} else if (isRecordAccessAuth(auth)) {
|
|
909
|
+
console.log('Record access auth:', auth.access);
|
|
910
|
+
} else if (isScopeAuth(auth)) {
|
|
911
|
+
console.log('Scope auth:', auth.scope);
|
|
912
|
+
}
|
|
913
|
+
}
|
|
914
|
+
```
|
|
915
|
+
|
|
916
|
+
### SurrealClient Interface
|
|
917
|
+
|
|
918
|
+
```typescript
|
|
919
|
+
interface SurrealClient {
|
|
920
|
+
connect(url: string): Promise<void>;
|
|
921
|
+
close(): Promise<void>;
|
|
922
|
+
use(params: { namespace: string; database: string }): Promise<void>;
|
|
923
|
+
signin(credentials: Record<string, unknown>): Promise<string>;
|
|
924
|
+
query<T = unknown>(sql: string, vars?: Record<string, unknown>): Promise<T[]>;
|
|
925
|
+
select<T = unknown>(thing: string): Promise<T[]>;
|
|
926
|
+
create<T = unknown>(thing: string, data?: Record<string, unknown>): Promise<T>;
|
|
927
|
+
insert<T = unknown>(thing: string, data?: Record<string, unknown> | Record<string, unknown>[]): Promise<T[]>;
|
|
928
|
+
update<T = unknown>(thing: string, data?: Record<string, unknown>): Promise<T>;
|
|
929
|
+
merge<T = unknown>(thing: string, data?: Record<string, unknown>): Promise<T>;
|
|
930
|
+
patch<T = unknown>(thing: string, data?: unknown[]): Promise<T>;
|
|
931
|
+
delete<T = unknown>(thing: string): Promise<T>;
|
|
932
|
+
}
|
|
933
|
+
```
|
|
934
|
+
|
|
935
|
+
## Troubleshooting / FAQ
|
|
936
|
+
|
|
937
|
+
### Connection Issues
|
|
938
|
+
|
|
939
|
+
**Q: I get "Failed to connect to SurrealDB" error**
|
|
940
|
+
|
|
941
|
+
A: Check the following:
|
|
942
|
+
1. Ensure SurrealDB is running and accessible at the configured URL
|
|
943
|
+
2. Verify the URL format is correct (include protocol: `http://`, `https://`, `ws://`, or `wss://`)
|
|
944
|
+
3. Check that namespace and database exist in SurrealDB
|
|
945
|
+
4. Verify authentication credentials are correct
|
|
946
|
+
|
|
947
|
+
```bash
|
|
948
|
+
# Test SurrealDB connectivity
|
|
949
|
+
curl -X POST http://localhost:8000/health
|
|
950
|
+
```
|
|
951
|
+
|
|
952
|
+
**Q: WebSocket connection keeps disconnecting**
|
|
953
|
+
|
|
954
|
+
A: This may be due to timeout settings. Increase the timeout:
|
|
955
|
+
|
|
956
|
+
```typescript
|
|
957
|
+
const config = defineSurrealConfig({
|
|
958
|
+
url: 'ws://localhost:8000',
|
|
959
|
+
namespace: 'test',
|
|
960
|
+
database: 'test',
|
|
961
|
+
timeout: 60000, // 60 seconds
|
|
962
|
+
});
|
|
963
|
+
```
|
|
964
|
+
|
|
965
|
+
### Authentication Issues
|
|
966
|
+
|
|
967
|
+
**Q: What authentication method should I use?**
|
|
968
|
+
|
|
969
|
+
A: It depends on your security requirements:
|
|
970
|
+
- **Root auth**: Full access, use for admin tasks or development
|
|
971
|
+
- **Namespace auth**: Access to all databases in a namespace
|
|
972
|
+
- **Database auth**: Access to a specific database
|
|
973
|
+
- **Record access auth** (2.x): Fine-grained access control per record
|
|
974
|
+
- **Scope auth** (1.x): User-level authentication with custom scopes
|
|
975
|
+
|
|
976
|
+
**Q: How do I authenticate users in my application?**
|
|
977
|
+
|
|
978
|
+
A: Use record access authentication (SurrealDB 2.x) or scope authentication (SurrealDB 1.x):
|
|
979
|
+
|
|
980
|
+
```typescript
|
|
981
|
+
// SurrealDB 2.x
|
|
982
|
+
const auth = recordAccessAuth('myns', 'mydb', 'user', {
|
|
983
|
+
email: userEmail,
|
|
984
|
+
password: userPassword,
|
|
985
|
+
});
|
|
986
|
+
|
|
987
|
+
// SurrealDB 1.x
|
|
988
|
+
const auth: ScopeAuth = {
|
|
989
|
+
namespace: 'myns',
|
|
990
|
+
database: 'mydb',
|
|
991
|
+
scope: 'user',
|
|
992
|
+
email: userEmail,
|
|
993
|
+
password: userPassword,
|
|
994
|
+
};
|
|
995
|
+
```
|
|
996
|
+
|
|
997
|
+
### Query Issues
|
|
998
|
+
|
|
999
|
+
**Q: How do I pass parameters to queries?**
|
|
1000
|
+
|
|
1001
|
+
A: Use numbered parameters ($0, $1, etc.):
|
|
1002
|
+
|
|
1003
|
+
```typescript
|
|
1004
|
+
const db = useDb(context);
|
|
1005
|
+
|
|
1006
|
+
// Parameters are converted to $0, $1, etc.
|
|
1007
|
+
const result = await db.client.query(
|
|
1008
|
+
'SELECT * FROM users WHERE email = $0 AND active = $1',
|
|
1009
|
+
['user@example.com', true]
|
|
1010
|
+
);
|
|
1011
|
+
```
|
|
1012
|
+
|
|
1013
|
+
**Q: Query deduplication is not working**
|
|
1014
|
+
|
|
1015
|
+
A: Ensure you're using the request-scoped client:
|
|
1016
|
+
|
|
1017
|
+
```typescript
|
|
1018
|
+
// Correct - uses deduplication
|
|
1019
|
+
const db = useDb(context);
|
|
1020
|
+
const result = await db.query('SELECT * FROM users');
|
|
1021
|
+
|
|
1022
|
+
// Direct client access - no deduplication
|
|
1023
|
+
const adapter = useAdapter(context);
|
|
1024
|
+
const client = adapter.getClient();
|
|
1025
|
+
const result = await client.query('SELECT * FROM users');
|
|
1026
|
+
```
|
|
1027
|
+
|
|
1028
|
+
### Transaction Issues
|
|
1029
|
+
|
|
1030
|
+
**Q: My transaction is not rolling back on error**
|
|
1031
|
+
|
|
1032
|
+
A: Ensure you're using the transaction helper or properly handling errors:
|
|
1033
|
+
|
|
1034
|
+
```typescript
|
|
1035
|
+
// Recommended approach - automatic rollback
|
|
1036
|
+
const result = await withTransaction(context, async (tx) => {
|
|
1037
|
+
// If any operation throws, the transaction is rolled back
|
|
1038
|
+
await tx.create('users', { name: 'Alice' });
|
|
1039
|
+
throw new Error('Something went wrong'); // Transaction will rollback
|
|
1040
|
+
});
|
|
1041
|
+
|
|
1042
|
+
// Manual approach - must handle rollback explicitly
|
|
1043
|
+
const tx = await adapter.beginTransaction();
|
|
1044
|
+
try {
|
|
1045
|
+
await tx.client.query('...');
|
|
1046
|
+
await tx.commit();
|
|
1047
|
+
} catch (error) {
|
|
1048
|
+
await tx.rollback(); // Don't forget this!
|
|
1049
|
+
throw error;
|
|
1050
|
+
}
|
|
1051
|
+
```
|
|
1052
|
+
|
|
1053
|
+
### Debug Mode
|
|
1054
|
+
|
|
1055
|
+
Enable debug logging to troubleshoot issues:
|
|
1056
|
+
|
|
1057
|
+
```typescript
|
|
1058
|
+
const config = defineSurrealConfig({
|
|
1059
|
+
url: 'http://localhost:8000',
|
|
1060
|
+
namespace: 'test',
|
|
1061
|
+
database: 'test',
|
|
1062
|
+
debug: true, // Logs connection events and queries
|
|
1063
|
+
});
|
|
1064
|
+
```
|
|
1065
|
+
|
|
1066
|
+
Debug output includes:
|
|
1067
|
+
- Connection attempts: `[surrealdb] Connecting to http://localhost:8000/rpc...`
|
|
1068
|
+
- Authentication: `[surrealdb] Signing in...`
|
|
1069
|
+
- Successful connection: `[surrealdb] Connected to test/test`
|
|
1070
|
+
- Query execution: `[surrealdb] Query: SELECT * FROM users {...}`
|
|
1071
|
+
- Disconnection: `[surrealdb] Disconnected`
|
|
1072
|
+
|
|
1073
|
+
### Error Classes
|
|
1074
|
+
|
|
1075
|
+
The adapter uses typed errors from `@ereo/db`:
|
|
1076
|
+
|
|
1077
|
+
```typescript
|
|
1078
|
+
import { ConnectionError, QueryError, TransactionError } from '@ereo/db-surrealdb';
|
|
1079
|
+
|
|
1080
|
+
try {
|
|
1081
|
+
const db = useDb(context);
|
|
1082
|
+
await db.client.query('INVALID SQL');
|
|
1083
|
+
} catch (error) {
|
|
1084
|
+
if (error instanceof ConnectionError) {
|
|
1085
|
+
console.error('Connection failed:', error.message);
|
|
1086
|
+
} else if (error instanceof QueryError) {
|
|
1087
|
+
console.error('Query failed:', error.message);
|
|
1088
|
+
console.error('SQL:', error.query);
|
|
1089
|
+
console.error('Params:', error.params);
|
|
1090
|
+
} else if (error instanceof TransactionError) {
|
|
1091
|
+
console.error('Transaction failed:', error.message);
|
|
1092
|
+
}
|
|
1093
|
+
}
|
|
1094
|
+
```
|