vibesql-server 0.1.2

Network server with PostgreSQL wire protocol for VibeSQL
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
# VibeSQL GraphQL API Documentation

## Overview

The VibeSQL GraphQL API provides an alternative query interface alongside the REST API. While not a full GraphQL implementation, it provides a lightweight GraphQL-like syntax for querying and mutating data in the database.

**Endpoint**: `POST /api/graphql`

## Request Format

All GraphQL requests are POST requests with a JSON body containing:

```json
{
  "query": "GraphQL query string",
  "variables": { /* optional variables */ },
  "operationName": "optional operation name"
}
```

## Response Format

All responses follow the GraphQL response format:

```json
{
  "data": { /* result data */ },
  "errors": [ /* optional error array */ ]
}
```

## Queries

### Simple SELECT Query

Query all records from a table:

```json
{
  "query": "{ users { id name email } }"
}
```

Response:

```json
{
  "data": {
    "data": [
      { "id": 1, "name": "Alice", "email": "alice@example.com" },
      { "id": 2, "name": "Bob", "email": "bob@example.com" }
    ]
  }
}
```

### Query with WHERE Clause

Filter records with a WHERE clause:

```json
{
  "query": "{ users(where: \"email = 'alice@example.com'\") { id name email } }"
}
```

### Query All Columns

Use `*` to select all columns:

```json
{
  "query": "{ users { * } }"
}
```

This returns all columns from the users table.

### Health Check Query

Check API health and version:

```json
{
  "query": "query { health { status version } }"
}
```

Response:

```json
{
  "data": {
    "status": "ok",
    "version": "0.1.1"
  }
}
```

## Nested Queries (Relationship Resolution)

VibeSQL automatically detects foreign key relationships between tables and allows nested queries to traverse these relationships. This avoids N+1 query problems by using batched queries.

### One-to-Many Relationships

Query a parent table with its related child records:

```json
{
  "query": "{ users { id name posts { id title } } }"
}
```

Response (each user includes their posts as a nested array):

```json
{
  "data": {
    "data": [
      {
        "id": 1,
        "name": "Alice",
        "posts": [
          { "id": 1, "title": "First Post" },
          { "id": 2, "title": "Second Post" }
        ]
      },
      {
        "id": 2,
        "name": "Bob",
        "posts": []
      }
    ]
  }
}
```

### Many-to-One Relationships

Query a child table with its related parent record:

```json
{
  "query": "{ posts { id title user { id name } } }"
}
```

Response (each post includes its author as a nested object):

```json
{
  "data": {
    "data": [
      {
        "id": 1,
        "title": "First Post",
        "user": { "id": 1, "name": "Alice" }
      },
      {
        "id": 2,
        "title": "Second Post",
        "user": { "id": 1, "name": "Alice" }
      }
    ]
  }
}
```

### Deep Nesting (3+ Levels)

Nested queries can be arbitrarily deep:

```json
{
  "query": "{ users { id name posts { id title comments { id body } } } }"
}
```

### Pagination on Nested Queries

Apply limits to nested queries using the `limit` and `offset` parameters:

```json
{
  "query": "{ users { id name posts(limit: 5, offset: 0) { id title } } }"
}
```

### How It Works

1. **Foreign Key Detection**: VibeSQL reads the schema to identify foreign key relationships
2. **Relationship Direction**:
   - **One-to-Many**: Parent table (e.g., `users`) → child table has FK (e.g., `posts.user_id`)
   - **Many-to-One**: Child table (e.g., `posts`) → parent table is referenced
3. **Batched Queries**: Nested data is fetched using `WHERE IN (...)` clauses to avoid N+1 problems
4. **Result Structuring**: Results are grouped and attached to parent records

### Schema Requirements

For relationship resolution to work, your schema must have defined foreign key constraints:

```sql
CREATE TABLE users (
    id INTEGER PRIMARY KEY,
    name VARCHAR(255)
);

CREATE TABLE posts (
    id INTEGER PRIMARY KEY,
    title VARCHAR(255),
    user_id INTEGER REFERENCES users(id)
);
```

## Mutations

### INSERT Mutation

Insert a new record:

```json
{
  "query": "mutation { insert(table: \"users\", values: {\"name\": \"Charlie\", \"email\": \"charlie@example.com\"}) }"
}
```

Response:

```json
{
  "data": {
    "rowsAffected": 1
  }
}
```

### UPDATE Mutation

Update existing records:

```json
{
  "query": "mutation { update(table: \"users\", values: {\"email\": \"newemail@example.com\"}, where: \"id = 1\") }"
}
```

Response:

```json
{
  "data": {
    "rowsAffected": 1
  }
}
```

### DELETE Mutation

Delete records (requires WHERE clause for safety):

```json
{
  "query": "mutation { delete(table: \"users\", where: \"id = 3\") }"
}
```

Response:

```json
{
  "data": {
    "rowsAffected": 1
  }
}
```

## Error Handling

If an error occurs, the response will include an `errors` array:

```json
{
  "data": null,
  "errors": [
    {
      "message": "Table 'invalid_table' not found"
    }
  ]
}
```

Common errors include:

- **Parse errors**: Invalid GraphQL syntax
- **Conversion errors**: Failed to convert GraphQL to SQL
- **Execution errors**: SQL query failed to execute
- **Validation errors**: Missing required parameters

## Data Type Conversion

JSON values are automatically converted to SQL types:

- **JSON null** → SQL `NULL`
- **JSON boolean** → SQL `BOOLEAN`
- **JSON number** → SQL `INTEGER` or `NUMERIC`
- **JSON string** → SQL `VARCHAR`

Note: Arrays and objects are not yet supported in parameter values.

## Limitations

The current GraphQL implementation has the following limitations:

1. **Limited type system** - No schema introspection
2. **Simple WHERE clauses** - Only string-based conditions
3. **No subscriptions** - Use REST `/api/subscribe` for real-time updates
4. **No aliases or fragments** - Basic queries only

For more complex operations, use the REST `/api/query` endpoint with raw SQL.

## Examples

### Complete INSERT Example

```bash
curl -X POST http://localhost:8080/api/graphql \
  -H "Content-Type: application/json" \
  -d '{
    "query": "mutation { insert(table: \"posts\", values: {\"title\": \"My Post\", \"author_id\": 1, \"content\": \"Hello World\"}) }"
  }'
```

### Complete SELECT Example

```bash
curl -X POST http://localhost:8080/api/graphql \
  -H "Content-Type: application/json" \
  -d '{
    "query": "{ posts(where: \"author_id = 1\") { id title content } }"
  }'
```

### Combined Query

```bash
curl -X POST http://localhost:8080/api/graphql \
  -H "Content-Type: application/json" \
  -d '{
    "query": "query { posts { id title author_id } users(where: \"status = active\") { id name } health { status } }"
  }'
```

## REST API Comparison

| Feature | GraphQL API | REST API |
|---------|-------------|----------|
| Query Language | GraphQL-like syntax | Raw SQL |
| Data Format | JSON objects with nested relations | JSON arrays |
| Error Handling | GraphQL errors | HTTP status codes |
| Complexity | Simple to moderate queries | Complex SQL |
| Relationships | Auto-resolved via foreign keys | Manual JOINs required |
| Nested Data | Built-in support | Not supported |
| Pagination | Supported on nested queries | Via LIMIT/OFFSET in SQL |
| Real-time Updates | Via REST API | `/api/subscribe` endpoint |

## Migration Guide: REST to GraphQL

### Simple REST Query

```json
POST /api/query
{
  "sql": "SELECT id, name FROM users WHERE active = true"
}
```

Equivalent GraphQL:

```json
POST /api/graphql
{
  "query": "{ users(where: \"active = true\") { id name } }"
}
```

### REST INSERT

```json
POST /api/query
{
  "sql": "INSERT INTO users (name, email) VALUES ($1, $2)",
  "params": ["Alice", "alice@example.com"]
}
```

Equivalent GraphQL:

```json
POST /api/graphql
{
  "query": "mutation { insert(table: \"users\", values: {\"name\": \"Alice\", \"email\": \"alice@example.com\"}) }"
}
```

## Architecture

The GraphQL implementation consists of three main components:

1. **GraphQLRequest/Response Types** - JSON serialization structures
2. **Query Parser** - Converts GraphQL syntax to internal representation
3. **SQL Generator** - Transforms GraphQL queries to SQL statements

The parser is intentionally simple to keep the implementation lightweight. For production use cases requiring full GraphQL features, consider using a dedicated GraphQL library or service.

## Performance Considerations

- **Direct SQL mapping** - GraphQL queries are translated to SQL with minimal overhead
- **Session creation** - Each request creates a new session; connection pooling recommended
- **WHERE clauses** - Complex conditions must be expressed as SQL strings
- **No caching** - Queries are executed immediately with no result caching

## Security

- **SQL Injection Prevention** - Parameter values are type-converted to prevent injection
- **WHERE clause validation** - Currently accepts any SQL expression (validate in application)
- **DELETE safety** - DELETE mutations require a WHERE clause

## Future Enhancements

Potential features for future versions:

- [ ] Full GraphQL schema introspection
- [x] Relationship traversal via foreign keys
- [x] Pagination with limit/offset on nested queries
- [ ] Aliases and query fragments
- [ ] GraphQL subscriptions via WebSocket
- [ ] Query batching
- [ ] Result caching
- [ ] DataLoader pattern for complex relationship queries