@xano/developer-mcp 1.0.5 → 1.0.7
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/dist/index.js +5 -0
- package/dist/templates/xanoscript-index.js +1 -0
- package/package.json +1 -1
- package/xanoscript_docs/README.md +53 -37
- package/xanoscript_docs/agents.md +13 -15
- package/xanoscript_docs/apis.md +25 -85
- package/xanoscript_docs/database.md +28 -31
- package/xanoscript_docs/ephemeral.md +5 -8
- package/xanoscript_docs/frontend.md +4 -7
- package/xanoscript_docs/functions.md +12 -63
- package/xanoscript_docs/integrations.md +5 -5
- package/xanoscript_docs/mcp-servers.md +10 -13
- package/xanoscript_docs/syntax.md +118 -48
- package/xanoscript_docs/tables.md +8 -11
- package/xanoscript_docs/tasks.md +6 -9
- package/xanoscript_docs/testing.md +5 -8
- package/xanoscript_docs/tools.md +3 -11
- package/xanoscript_docs/triggers.md +532 -0
- package/xanoscript_docs/types.md +21 -31
- package/xanoscript_docs/plan.md +0 -192
package/dist/index.js
CHANGED
|
@@ -47,6 +47,11 @@ const XANOSCRIPT_DOCS_V2 = {
|
|
|
47
47
|
applyTo: ["tasks/*.xs"],
|
|
48
48
|
description: "Scheduled and cron jobs",
|
|
49
49
|
},
|
|
50
|
+
triggers: {
|
|
51
|
+
file: "triggers.md",
|
|
52
|
+
applyTo: ["triggers/**/*.xs"],
|
|
53
|
+
description: "Event-driven handlers (table, realtime, workspace, agent, MCP)",
|
|
54
|
+
},
|
|
50
55
|
database: {
|
|
51
56
|
file: "database.md",
|
|
52
57
|
applyTo: ["functions/**/*.xs", "apis/**/*.xs", "tasks/*.xs", "tools/**/*.xs"],
|
|
@@ -22,6 +22,7 @@ ${formatRow("function", "Custom reusable functions in `functions/`")}
|
|
|
22
22
|
${formatRow("api_query", "HTTP API endpoints in `apis/`")}
|
|
23
23
|
${formatRow("table", "Database table schemas in `tables/`")}
|
|
24
24
|
${formatRow("task", "Scheduled background tasks in `tasks/`")}
|
|
25
|
+
${formatRow("triggers", "Event-driven handlers in `triggers/`")}
|
|
25
26
|
${formatRow("tool", "AI-callable tools in `tools/`")}
|
|
26
27
|
${formatRow("agent", "AI agents in `agents/`")}
|
|
27
28
|
${formatRow("mcp_server", "MCP servers in `mcp_servers/`")}
|
package/package.json
CHANGED
|
@@ -10,26 +10,30 @@ XanoScript is the declarative scripting language for [Xano](https://xano.com), a
|
|
|
10
10
|
| `function` | `functions/**/*.xs` | Reusable logic blocks |
|
|
11
11
|
| `query` | `apis/<group>/*.xs` | HTTP API endpoints |
|
|
12
12
|
| `task` | `tasks/*.xs` | Scheduled/cron jobs |
|
|
13
|
+
| `*_trigger` | `triggers/**/*.xs` | Event-driven handlers |
|
|
13
14
|
| `agent` | `agents/**/*.xs` | AI-powered agents |
|
|
14
15
|
| `tool` | `tools/**/*.xs` | Tools for AI agents |
|
|
15
16
|
| `mcp_server` | `mcp_servers/**/*.xs` | MCP server definitions |
|
|
16
17
|
| `addon` | `addons/*.xs` | Subqueries for related data |
|
|
17
18
|
|
|
19
|
+
**Important:** Each `.xs` file must contain exactly one definition. You cannot define multiple tables, functions, queries, or other constructs in a single file.
|
|
20
|
+
|
|
18
21
|
## Workspace Structure
|
|
19
22
|
|
|
20
23
|
```
|
|
21
24
|
project/
|
|
22
|
-
├── tables/
|
|
23
|
-
├── functions/
|
|
25
|
+
├── tables/ // Database table schemas
|
|
26
|
+
├── functions/ // Reusable functions (supports subfolders)
|
|
24
27
|
├── apis/
|
|
25
|
-
│ └── <api-group>/
|
|
26
|
-
├── tasks/
|
|
27
|
-
├──
|
|
28
|
-
├──
|
|
29
|
-
├──
|
|
30
|
-
├──
|
|
31
|
-
├──
|
|
32
|
-
|
|
28
|
+
│ └── <api-group>/ // API endpoints grouped by domain
|
|
29
|
+
├── tasks/ // Scheduled jobs
|
|
30
|
+
├── triggers/ // Event-driven handlers
|
|
31
|
+
├── agents/ // AI agents
|
|
32
|
+
├── tools/ // AI tools
|
|
33
|
+
├── mcp_servers/ // MCP server definitions
|
|
34
|
+
├── addons/ // Query addons
|
|
35
|
+
├── static/ // Frontend files (HTML, CSS, JS)
|
|
36
|
+
└── ephemeral/ // Temporary test environments
|
|
33
37
|
```
|
|
34
38
|
|
|
35
39
|
## Environment Variables
|
|
@@ -53,28 +57,36 @@ Custom environment variables are set in the Xano dashboard and accessed as `$env
|
|
|
53
57
|
### Block Structure
|
|
54
58
|
```xs
|
|
55
59
|
<construct> "<name>" {
|
|
56
|
-
input { ... }
|
|
57
|
-
stack { ... }
|
|
58
|
-
response = $var
|
|
60
|
+
input { ... } // Parameters (optional)
|
|
61
|
+
stack { ... } // Logic
|
|
62
|
+
response = $var // Output
|
|
59
63
|
}
|
|
60
64
|
```
|
|
61
65
|
|
|
62
66
|
### Variable Access
|
|
63
67
|
```xs
|
|
64
|
-
$input.field
|
|
65
|
-
$var.field
|
|
66
|
-
$auth.id
|
|
67
|
-
$env.MY_VAR
|
|
68
|
-
$db.table.field
|
|
69
|
-
$this
|
|
68
|
+
$input.field // Input parameters
|
|
69
|
+
$var.field // Stack variables
|
|
70
|
+
$auth.id // Authenticated user ID
|
|
71
|
+
$env.MY_VAR // Environment variable
|
|
72
|
+
$db.table.field // Database field reference (in queries)
|
|
73
|
+
$this // Current item in loops/maps
|
|
70
74
|
```
|
|
71
75
|
|
|
76
|
+
### Comments
|
|
77
|
+
```xs
|
|
78
|
+
// Single-line comment
|
|
79
|
+
var $total { value = 0 } // Inline comment
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
**Note:** XanoScript only supports `//` for comments. Other comment styles like `#` are not supported.
|
|
83
|
+
|
|
72
84
|
### Filters (Pipe Syntax)
|
|
73
85
|
```xs
|
|
74
|
-
$value|trim|lower
|
|
75
|
-
$input.name|strlen
|
|
76
|
-
$array|first
|
|
77
|
-
($a + $b)|round:2
|
|
86
|
+
$value|trim|lower // Chain filters
|
|
87
|
+
$input.name|strlen // Get length
|
|
88
|
+
$array|first // First element
|
|
89
|
+
($a + $b)|round:2 // Math with precision
|
|
78
90
|
```
|
|
79
91
|
|
|
80
92
|
## File Frontmatter
|
|
@@ -91,17 +103,21 @@ This helps AI tools apply the correct documentation based on the file being edit
|
|
|
91
103
|
|
|
92
104
|
## Documentation Index
|
|
93
105
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
106
|
+
Use `xanoscript_docs({ keyword: "<keyword>" })` to retrieve documentation.
|
|
107
|
+
|
|
108
|
+
| Topic | Keyword | Description |
|
|
109
|
+
|-------|---------|-------------|
|
|
110
|
+
| Syntax Reference | `syntax` | Expressions, operators, filters |
|
|
111
|
+
| Types & Inputs | `input` | Data types, validation, input blocks |
|
|
112
|
+
| Tables | `table` | Database schema definitions |
|
|
113
|
+
| Functions | `function` | Reusable function stacks |
|
|
114
|
+
| APIs | `api_query` | HTTP endpoint definitions |
|
|
115
|
+
| Tasks | `task` | Scheduled jobs |
|
|
116
|
+
| Triggers | `trigger` | Event-driven handlers (table, realtime, workspace, agent, MCP) |
|
|
117
|
+
| Database Operations | `db_query` | Query, add, edit, delete |
|
|
118
|
+
| Agents | `agent` | AI agent configuration |
|
|
119
|
+
| Tools | `tool` | AI tools for agents |
|
|
120
|
+
| MCP Servers | `mcp_server` | Model Context Protocol servers |
|
|
121
|
+
| Testing | `testing` | Unit tests and mocking |
|
|
122
|
+
| Frontend | `frontend` | Static frontend development |
|
|
123
|
+
| Ephemeral | `ephemeral` | Temporary environments
|
|
@@ -71,10 +71,10 @@ ai.agent.run "Customer Support" {
|
|
|
71
71
|
|
|
72
72
|
```xs
|
|
73
73
|
llm = {
|
|
74
|
-
type: "<provider>"
|
|
75
|
-
system_prompt: "..."
|
|
76
|
-
prompt: "{{ $args.input }}"
|
|
77
|
-
max_steps: 5
|
|
74
|
+
type: "<provider>" // Required
|
|
75
|
+
system_prompt: "..." // Agent persona and rules
|
|
76
|
+
prompt: "{{ $args.input }}" // User input template
|
|
77
|
+
max_steps: 5 // Max LLM calls per run
|
|
78
78
|
}
|
|
79
79
|
```
|
|
80
80
|
|
|
@@ -94,7 +94,7 @@ llm = {
|
|
|
94
94
|
prompt: "{{ $args.message }}"
|
|
95
95
|
max_steps: 3
|
|
96
96
|
temperature: 0
|
|
97
|
-
search_grounding: false
|
|
97
|
+
search_grounding: false // Google Search grounding
|
|
98
98
|
}
|
|
99
99
|
```
|
|
100
100
|
|
|
@@ -108,7 +108,7 @@ llm = {
|
|
|
108
108
|
prompt: "{{ $args.message }}"
|
|
109
109
|
max_steps: 5
|
|
110
110
|
temperature: 0.2
|
|
111
|
-
thinking_tokens: 10000
|
|
111
|
+
thinking_tokens: 10000 // Extended thinking
|
|
112
112
|
include_thoughts: true
|
|
113
113
|
}
|
|
114
114
|
```
|
|
@@ -123,7 +123,7 @@ llm = {
|
|
|
123
123
|
prompt: "{{ $args.message }}"
|
|
124
124
|
max_steps: 5
|
|
125
125
|
temperature: 0.8
|
|
126
|
-
reasoning_effort: "medium"
|
|
126
|
+
reasoning_effort: "medium" // low, medium, high
|
|
127
127
|
}
|
|
128
128
|
```
|
|
129
129
|
|
|
@@ -134,7 +134,7 @@ llm = {
|
|
|
134
134
|
api_key: "{{ $env.GROQ_API_KEY }}"
|
|
135
135
|
baseURL: "https://api.groq.com/openai/v1"
|
|
136
136
|
model: "llama-3.3-70b-versatile"
|
|
137
|
-
compatibility: "compatible"
|
|
137
|
+
compatibility: "compatible" // Required for non-OpenAI
|
|
138
138
|
...
|
|
139
139
|
}
|
|
140
140
|
```
|
|
@@ -151,7 +151,7 @@ llm = {
|
|
|
151
151
|
prompt: "{{ $args.message }}"
|
|
152
152
|
max_steps: 8
|
|
153
153
|
temperature: 0.3
|
|
154
|
-
send_reasoning: true
|
|
154
|
+
send_reasoning: true // Include thinking blocks
|
|
155
155
|
}
|
|
156
156
|
```
|
|
157
157
|
|
|
@@ -218,9 +218,9 @@ llm = {
|
|
|
218
218
|
|
|
219
219
|
### Available Variables
|
|
220
220
|
```xs
|
|
221
|
-
{{ $args.any_arg }}
|
|
222
|
-
{{ $env.MY_VAR }}
|
|
223
|
-
{{ "now"|date("Y-m-d") }}
|
|
221
|
+
{{ $args.any_arg }} // Runtime arguments
|
|
222
|
+
{{ $env.MY_VAR }} // Environment variables
|
|
223
|
+
{{ "now"|date("Y-m-d") }} // Current date
|
|
224
224
|
```
|
|
225
225
|
|
|
226
226
|
---
|
|
@@ -324,6 +324,4 @@ agent "Research Assistant" {
|
|
|
324
324
|
3. **Limit max_steps** - Prevent infinite loops (3-10 typical)
|
|
325
325
|
4. **Don't repeat tool descriptions** - They're auto-injected
|
|
326
326
|
5. **Use environment variables** - Never hardcode API keys
|
|
327
|
-
6. **
|
|
328
|
-
7. **Test with xano-free first** - Free for development
|
|
329
|
-
8. **Use structured outputs** - When you need consistent JSON
|
|
327
|
+
6. **Test with xano-free first** - Free for development
|
package/xanoscript_docs/apis.md
CHANGED
|
@@ -9,19 +9,19 @@ HTTP endpoint definitions in XanoScript.
|
|
|
9
9
|
## Quick Reference
|
|
10
10
|
|
|
11
11
|
```xs
|
|
12
|
-
query "
|
|
13
|
-
api_group = "<GroupName>"
|
|
12
|
+
query "endpoint-path" verb=<METHOD> {
|
|
13
|
+
api_group = "<GroupName>" // Required: API group for organization
|
|
14
14
|
description = "What this endpoint does"
|
|
15
|
-
auth = "<table>"
|
|
15
|
+
auth = "<table>" // Optional: require authentication
|
|
16
16
|
input { ... }
|
|
17
17
|
stack { ... }
|
|
18
18
|
response = $result
|
|
19
19
|
}
|
|
20
20
|
```
|
|
21
21
|
|
|
22
|
-
### Query Name (Required)
|
|
22
|
+
### Query Name (Required, Non-Empty)
|
|
23
23
|
|
|
24
|
-
The query name is **required** and
|
|
24
|
+
The query name is **required** and **must be a non-empty string**. Empty names (`query "" verb=...`) are invalid. The name defines the endpoint path after the API group canonical.
|
|
25
25
|
|
|
26
26
|
**Full URL path structure:**
|
|
27
27
|
```
|
|
@@ -38,61 +38,28 @@ The query name can include slashes for nested paths:
|
|
|
38
38
|
|
|
39
39
|
### API Groups (Required)
|
|
40
40
|
|
|
41
|
-
Every
|
|
42
|
-
|
|
43
|
-
**Two requirements for API groups:**
|
|
44
|
-
1. Each `query` definition **must** include an `api_group` property specifying which group it belongs to
|
|
45
|
-
2. API groups are organized as folders within `apis/`, each with an `api_group.xs` file
|
|
46
|
-
|
|
47
|
-
- API groups appear as top-level folders under `apis/`
|
|
48
|
-
- Each group **must** have an `api_group.xs` file that defines the group
|
|
49
|
-
- The group contains `.xs` files defining individual endpoints
|
|
50
|
-
- The group name becomes part of the endpoint URL path
|
|
51
|
-
- You cannot create endpoints directly in the `apis/` root folder
|
|
52
|
-
|
|
53
|
-
#### Defining an API Group
|
|
54
|
-
|
|
55
|
-
Create an `api_group.xs` file in the group folder:
|
|
41
|
+
Every endpoint must belong to an API group. Each group is a folder with an `api_group.xs` file:
|
|
56
42
|
|
|
57
43
|
```xs
|
|
58
44
|
api_group "users" {
|
|
59
|
-
canonical = "users"
|
|
60
|
-
description = "User management
|
|
45
|
+
canonical = "users" // Required: URL path segment
|
|
46
|
+
description = "User management" // Optional
|
|
61
47
|
}
|
|
62
48
|
```
|
|
63
49
|
|
|
64
|
-
#### API Group Properties
|
|
65
|
-
|
|
66
|
-
| Property | Required | Description |
|
|
67
|
-
|----------|----------|-------------|
|
|
68
|
-
| `canonical` | **Yes** | The URL path segment for this group. You cannot access a Xano API without it. |
|
|
69
|
-
| `description` | No | What this API group contains |
|
|
70
|
-
|
|
71
|
-
```xs
|
|
72
|
-
api_group "events" {
|
|
73
|
-
canonical = "events-api" # Required: URL will use /events-api
|
|
74
|
-
description = "Event management"
|
|
75
|
-
}
|
|
76
|
-
```
|
|
77
|
-
|
|
78
|
-
> **Important:** The `canonical` property is required. It defines the base path for all endpoints in this group.
|
|
79
|
-
|
|
80
50
|
### File Structure
|
|
81
51
|
```
|
|
82
52
|
apis/
|
|
83
|
-
├── users/
|
|
84
|
-
│ ├── api_group.xs
|
|
85
|
-
│ ├── list.xs
|
|
86
|
-
│
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
└── search.xs # GET /products/search
|
|
53
|
+
├── users/
|
|
54
|
+
│ ├── api_group.xs // Defines group (canonical = "users")
|
|
55
|
+
│ ├── list.xs // GET /users/list
|
|
56
|
+
│ └── by-id.xs // GET/PATCH/DELETE /users/{id}
|
|
57
|
+
└── products/
|
|
58
|
+
├── api_group.xs // Defines group (canonical = "products")
|
|
59
|
+
└── search.xs // GET /products/search
|
|
91
60
|
```
|
|
92
61
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
> **Note:** Files placed directly in `apis/` without a group folder are invalid. Each API group folder must contain an `api_group.xs` file.
|
|
62
|
+
Full URL: `/<canonical>/<query name>` (e.g., `/users/profile`)
|
|
96
63
|
|
|
97
64
|
---
|
|
98
65
|
|
|
@@ -132,11 +99,11 @@ query "status" verb=GET {
|
|
|
132
99
|
```xs
|
|
133
100
|
query "profile" verb=GET {
|
|
134
101
|
api_group = "Users"
|
|
135
|
-
auth = "user"
|
|
102
|
+
auth = "user" // Requires valid JWT
|
|
136
103
|
stack {
|
|
137
104
|
db.get "user" {
|
|
138
105
|
field_name = "id"
|
|
139
|
-
field_value = $auth.id
|
|
106
|
+
field_value = $auth.id // User ID from token
|
|
140
107
|
} as $user
|
|
141
108
|
}
|
|
142
109
|
response = $user
|
|
@@ -354,33 +321,11 @@ stack {
|
|
|
354
321
|
|
|
355
322
|
## Error Handling
|
|
356
323
|
|
|
357
|
-
|
|
358
|
-
```xs
|
|
359
|
-
stack {
|
|
360
|
-
precondition ($input.amount > 0) {
|
|
361
|
-
error_type = "inputerror"
|
|
362
|
-
error = "Amount must be positive"
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
precondition ($user != null) {
|
|
366
|
-
error_type = "notfound"
|
|
367
|
-
error = "User not found"
|
|
368
|
-
}
|
|
369
|
-
|
|
370
|
-
precondition ($user.id == $auth.id) {
|
|
371
|
-
error_type = "accessdenied"
|
|
372
|
-
error = "Not authorized"
|
|
373
|
-
}
|
|
374
|
-
}
|
|
375
|
-
```
|
|
324
|
+
For complete error handling reference, use `xanoscript_docs({ keyword: "syntax" })`.
|
|
376
325
|
|
|
377
|
-
### Error Types
|
|
378
326
|
| Type | HTTP Status |
|
|
379
327
|
|------|-------------|
|
|
380
|
-
| `inputerror` | 400
|
|
381
|
-
| `accessdenied` | 403 Forbidden |
|
|
382
|
-
| `notfound` | 404 Not Found |
|
|
383
|
-
| `standard` | 500 Internal Server Error |
|
|
328
|
+
| `inputerror` | 400 | `accessdenied` | 403 | `notfound` | 404 | `standard` | 500 |
|
|
384
329
|
|
|
385
330
|
---
|
|
386
331
|
|
|
@@ -404,13 +349,8 @@ When using `return = { type: "list", paging: {...} }`:
|
|
|
404
349
|
|
|
405
350
|
## Best Practices
|
|
406
351
|
|
|
407
|
-
1. **
|
|
408
|
-
2. **
|
|
409
|
-
3. **
|
|
410
|
-
4. **
|
|
411
|
-
5. **
|
|
412
|
-
6. **Validate inputs** - Use filters and preconditions
|
|
413
|
-
7. **Return appropriate errors** - Use correct error types
|
|
414
|
-
8. **Paginate lists** - Never return unbounded lists
|
|
415
|
-
9. **Document with description** - Explain what endpoint does
|
|
416
|
-
10. **Group related endpoints** - Organize by resource in api groups
|
|
352
|
+
1. **RESTful design** - Use appropriate HTTP methods for operations
|
|
353
|
+
2. **Consistent naming** - Use lowercase, hyphens for multi-word paths
|
|
354
|
+
3. **Authenticate writes** - Always require auth for POST/PATCH/DELETE
|
|
355
|
+
4. **Paginate lists** - Never return unbounded result sets
|
|
356
|
+
5. **Group by resource** - Organize endpoints in logical api groups
|
|
@@ -35,7 +35,7 @@ db.query "product" {
|
|
|
35
35
|
|
|
36
36
|
### Where Operators
|
|
37
37
|
```xs
|
|
38
|
-
|
|
38
|
+
// Comparison
|
|
39
39
|
$db.product.price == 100
|
|
40
40
|
$db.product.price != 0
|
|
41
41
|
$db.product.price > 50
|
|
@@ -43,32 +43,32 @@ $db.product.price >= 50
|
|
|
43
43
|
$db.product.price < 100
|
|
44
44
|
$db.product.price <= 100
|
|
45
45
|
|
|
46
|
-
|
|
46
|
+
// Null-safe (ignore if value is null)
|
|
47
47
|
$db.product.category ==? $input.category
|
|
48
48
|
$db.product.price >=? $input.min_price
|
|
49
49
|
|
|
50
|
-
|
|
51
|
-
$db.product.name includes "phone"
|
|
52
|
-
$db.product.tags contains "featured"
|
|
50
|
+
// String matching
|
|
51
|
+
$db.product.name includes "phone" // Contains substring
|
|
52
|
+
$db.product.tags contains "featured" // Array contains value
|
|
53
53
|
$db.product.tags not contains "hidden"
|
|
54
54
|
|
|
55
|
-
|
|
55
|
+
// Array overlap
|
|
56
56
|
$db.product.tags overlaps ["a", "b"]
|
|
57
57
|
$db.product.tags not overlaps ["x", "y"]
|
|
58
58
|
|
|
59
|
-
|
|
59
|
+
// Combining conditions
|
|
60
60
|
$db.product.is_active == true && $db.product.price > 0
|
|
61
61
|
$db.product.category == "electronics" || $db.product.featured == true
|
|
62
62
|
```
|
|
63
63
|
|
|
64
64
|
### Return Types
|
|
65
65
|
```xs
|
|
66
|
-
|
|
66
|
+
// List (default)
|
|
67
67
|
db.query "product" {
|
|
68
68
|
return = { type: "list" }
|
|
69
69
|
} as $products
|
|
70
70
|
|
|
71
|
-
|
|
71
|
+
// With pagination
|
|
72
72
|
db.query "product" {
|
|
73
73
|
return = {
|
|
74
74
|
type: "list",
|
|
@@ -76,19 +76,19 @@ db.query "product" {
|
|
|
76
76
|
}
|
|
77
77
|
} as $products
|
|
78
78
|
|
|
79
|
-
|
|
79
|
+
// Single record
|
|
80
80
|
db.query "product" {
|
|
81
81
|
where = $db.product.sku == $input.sku
|
|
82
82
|
return = { type: "single" }
|
|
83
83
|
} as $product
|
|
84
84
|
|
|
85
|
-
|
|
85
|
+
// Count
|
|
86
86
|
db.query "product" {
|
|
87
87
|
where = $db.product.is_active == true
|
|
88
88
|
return = { type: "count" }
|
|
89
89
|
} as $count
|
|
90
90
|
|
|
91
|
-
|
|
91
|
+
// Exists
|
|
92
92
|
db.query "product" {
|
|
93
93
|
where = $db.product.email == $input.email
|
|
94
94
|
return = { type: "exists" }
|
|
@@ -98,9 +98,9 @@ db.query "product" {
|
|
|
98
98
|
### Sorting
|
|
99
99
|
```xs
|
|
100
100
|
db.query "product" {
|
|
101
|
-
sort = { created_at: "desc" }
|
|
102
|
-
sort = { name: "asc" }
|
|
103
|
-
sort = { id: "rand" }
|
|
101
|
+
sort = { created_at: "desc" } // Descending
|
|
102
|
+
sort = { name: "asc" } // Ascending
|
|
103
|
+
sort = { id: "rand" } // Random
|
|
104
104
|
} as $products
|
|
105
105
|
```
|
|
106
106
|
|
|
@@ -110,7 +110,7 @@ db.query "comment" {
|
|
|
110
110
|
join = {
|
|
111
111
|
post: {
|
|
112
112
|
table: "post",
|
|
113
|
-
type: "inner",
|
|
113
|
+
type: "inner", // inner, left, right
|
|
114
114
|
where: $db.comment.post_id == $db.post.id
|
|
115
115
|
}
|
|
116
116
|
}
|
|
@@ -279,7 +279,7 @@ Delete all records from a table.
|
|
|
279
279
|
|
|
280
280
|
```xs
|
|
281
281
|
db.truncate "temp_data" {
|
|
282
|
-
reset = true
|
|
282
|
+
reset = true // Reset auto-increment
|
|
283
283
|
}
|
|
284
284
|
```
|
|
285
285
|
|
|
@@ -293,7 +293,7 @@ Execute raw SQL (use sparingly).
|
|
|
293
293
|
db.direct_query {
|
|
294
294
|
sql = "SELECT * FROM users WHERE email = ? AND status = ?"
|
|
295
295
|
arg = [$input.email, "active"]
|
|
296
|
-
response_type = "list"
|
|
296
|
+
response_type = "list" // list or single
|
|
297
297
|
} as $results
|
|
298
298
|
```
|
|
299
299
|
|
|
@@ -339,9 +339,9 @@ Filters for use in `where` clauses:
|
|
|
339
339
|
|
|
340
340
|
### String/Text
|
|
341
341
|
```xs
|
|
342
|
-
$db.name|to_lower
|
|
343
|
-
$db.name|concat:" "
|
|
344
|
-
$db.text|substr:0:100
|
|
342
|
+
$db.name|to_lower // Case conversion
|
|
343
|
+
$db.name|concat:" " // Concatenation
|
|
344
|
+
$db.text|substr:0:100 // Substring
|
|
345
345
|
```
|
|
346
346
|
|
|
347
347
|
### Numeric
|
|
@@ -363,8 +363,8 @@ $db.created_at|timestamp_subtract_hours:24
|
|
|
363
363
|
|
|
364
364
|
### Geographic
|
|
365
365
|
```xs
|
|
366
|
-
$db.location|distance:$input.point
|
|
367
|
-
$db.location|within:$input.point:1000
|
|
366
|
+
$db.location|distance:$input.point // Distance in meters
|
|
367
|
+
$db.location|within:$input.point:1000 // Within radius
|
|
368
368
|
```
|
|
369
369
|
|
|
370
370
|
### Vector (AI/ML)
|
|
@@ -385,7 +385,7 @@ $db.content|search_rank:$input.query
|
|
|
385
385
|
Connect to external databases:
|
|
386
386
|
|
|
387
387
|
```xs
|
|
388
|
-
|
|
388
|
+
// PostgreSQL
|
|
389
389
|
db.external.postgres.direct_query {
|
|
390
390
|
connection_string = $env.EXTERNAL_PG_URL
|
|
391
391
|
sql = "SELECT * FROM users WHERE id = ?"
|
|
@@ -393,13 +393,13 @@ db.external.postgres.direct_query {
|
|
|
393
393
|
response_type = "single"
|
|
394
394
|
} as $user
|
|
395
395
|
|
|
396
|
-
|
|
396
|
+
// MySQL
|
|
397
397
|
db.external.mysql.direct_query { ... }
|
|
398
398
|
|
|
399
|
-
|
|
399
|
+
// MS SQL
|
|
400
400
|
db.external.mssql.direct_query { ... }
|
|
401
401
|
|
|
402
|
-
|
|
402
|
+
// Oracle
|
|
403
403
|
db.external.oracle.direct_query { ... }
|
|
404
404
|
```
|
|
405
405
|
|
|
@@ -411,7 +411,4 @@ db.external.oracle.direct_query { ... }
|
|
|
411
411
|
2. **Use db.get for single lookups** - Simpler than db.query with single return
|
|
412
412
|
3. **Use db.patch for dynamic updates** - Accepts variable data
|
|
413
413
|
4. **Use transactions for atomicity** - Ensure all-or-nothing operations
|
|
414
|
-
5. **
|
|
415
|
-
6. **Use null-safe operators** - `==?` for optional filters
|
|
416
|
-
7. **Paginate results** - Never return unbounded lists
|
|
417
|
-
8. **Validate before delete** - Check ownership/permissions
|
|
414
|
+
5. **Use null-safe operators** - `==?` for optional filters
|
|
@@ -116,8 +116,8 @@ One-time operation with setup and cleanup hooks.
|
|
|
116
116
|
```xs
|
|
117
117
|
function "$main" {
|
|
118
118
|
input {
|
|
119
|
-
json args
|
|
120
|
-
json pre
|
|
119
|
+
json args // Runtime arguments
|
|
120
|
+
json pre // Result from $pre
|
|
121
121
|
}
|
|
122
122
|
stack {
|
|
123
123
|
db.query authors {
|
|
@@ -325,9 +325,6 @@ publish_ephemeral_service name="migration" directory="ephemeral/my-job" mode="jo
|
|
|
325
325
|
|
|
326
326
|
1. **Keep isolated** - Self-contained with own tables/functions
|
|
327
327
|
2. **Seed test data** - Use `items` in table definitions
|
|
328
|
-
3. **
|
|
329
|
-
4. **
|
|
330
|
-
5. **
|
|
331
|
-
6. **Use preconditions** - Verify expected outcomes in jobs
|
|
332
|
-
7. **Name clearly** - Indicate purpose: `auth-test`, `data-migration`
|
|
333
|
-
8. **Set end dates** - Use `ends_on` for temporary schedules
|
|
328
|
+
3. **Handle cleanup** - Use `$post` for notifications/cleanup
|
|
329
|
+
4. **Validate in $pre** - Check preconditions before main logic
|
|
330
|
+
5. **Name clearly** - Indicate purpose: `auth-test`, `data-migration`
|
|
@@ -11,10 +11,10 @@ Static frontend development and Lovable/Supabase migration.
|
|
|
11
11
|
### Directory Structure
|
|
12
12
|
```
|
|
13
13
|
static/
|
|
14
|
-
├── index.html
|
|
14
|
+
├── index.html // Main entry point
|
|
15
15
|
├── css/
|
|
16
16
|
├── js/
|
|
17
|
-
│ └── api.js
|
|
17
|
+
│ └── api.js // Centralized API calls
|
|
18
18
|
└── assets/
|
|
19
19
|
```
|
|
20
20
|
|
|
@@ -284,8 +284,5 @@ The tool will:
|
|
|
284
284
|
|
|
285
285
|
1. **Get API specs first** - Use `get_xano_api_specifications` before coding
|
|
286
286
|
2. **Centralize API calls** - Single `api.js` file
|
|
287
|
-
3. **
|
|
288
|
-
4. **
|
|
289
|
-
5. **Validate before submit** - Client-side validation before API calls
|
|
290
|
-
6. **Use loading states** - Show progress during API calls
|
|
291
|
-
7. **Test incrementally** - Migrate one feature at a time
|
|
287
|
+
3. **Store tokens securely** - localStorage for web, secure storage for mobile
|
|
288
|
+
4. **Test incrementally** - Migrate one feature at a time
|