@xano/developer-mcp 1.0.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/LICENSE +21 -0
- package/README.md +261 -0
- package/api_docs/addon.md +193 -0
- package/api_docs/agent.md +154 -0
- package/api_docs/api_group.md +236 -0
- package/api_docs/authentication.md +68 -0
- package/api_docs/file.md +190 -0
- package/api_docs/function.md +217 -0
- package/api_docs/history.md +263 -0
- package/api_docs/index.md +104 -0
- package/api_docs/mcp_server.md +139 -0
- package/api_docs/middleware.md +205 -0
- package/api_docs/realtime.md +153 -0
- package/api_docs/table.md +151 -0
- package/api_docs/task.md +191 -0
- package/api_docs/tool.md +216 -0
- package/api_docs/triggers.md +344 -0
- package/api_docs/workspace.md +246 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +495 -0
- package/package.json +49 -0
- package/xanoscript_docs/README.md +1 -0
- package/xanoscript_docs/api_query_examples.md +1255 -0
- package/xanoscript_docs/api_query_guideline.md +129 -0
- package/xanoscript_docs/build_from_lovable.md +715 -0
- package/xanoscript_docs/db_query_guideline.md +427 -0
- package/xanoscript_docs/ephemeral_environment_guideline.md +529 -0
- package/xanoscript_docs/expression_guideline.md +1086 -0
- package/xanoscript_docs/frontend_guideline.md +67 -0
- package/xanoscript_docs/function_examples.md +1406 -0
- package/xanoscript_docs/function_guideline.md +130 -0
- package/xanoscript_docs/functions.md +2155 -0
- package/xanoscript_docs/input_guideline.md +227 -0
- package/xanoscript_docs/mcp_server_examples.md +36 -0
- package/xanoscript_docs/mcp_server_guideline.md +69 -0
- package/xanoscript_docs/query_filter.md +489 -0
- package/xanoscript_docs/table_examples.md +586 -0
- package/xanoscript_docs/table_guideline.md +137 -0
- package/xanoscript_docs/task_examples.md +511 -0
- package/xanoscript_docs/task_guideline.md +103 -0
- package/xanoscript_docs/tips_and_tricks.md +144 -0
- package/xanoscript_docs/tool_examples.md +69 -0
- package/xanoscript_docs/tool_guideline.md +139 -0
- package/xanoscript_docs/unit_testing_guideline.md +328 -0
- package/xanoscript_docs/version.json +3 -0
- package/xanoscript_docs/workspace.md +17 -0
|
@@ -0,0 +1,427 @@
|
|
|
1
|
+
# Xano Database Query Guidelines
|
|
2
|
+
|
|
3
|
+
## db.query
|
|
4
|
+
|
|
5
|
+
The `db.query` is the most used and the most flexible database query, it is used to retrieve data from your database (not unlike a SQL `SELECT` statement). It allows you to specify the table, filters, sorting, and pagination options.
|
|
6
|
+
|
|
7
|
+
### Search Argument
|
|
8
|
+
|
|
9
|
+
The `search` is the equivalent of a SQL `WHERE` clause. It allows you to filter records based on specific conditions. The list of available operators includes:
|
|
10
|
+
|
|
11
|
+
#### Basic Comparison Operators
|
|
12
|
+
|
|
13
|
+
Basic comparison operators are:
|
|
14
|
+
|
|
15
|
+
- `==` (equals)
|
|
16
|
+
- `!=` (not equals)
|
|
17
|
+
- `>` (greater than)
|
|
18
|
+
- `>=` (greater than or equal)
|
|
19
|
+
- `<` (less than)
|
|
20
|
+
- `<=` (less than or equal)
|
|
21
|
+
|
|
22
|
+
**Examples**
|
|
23
|
+
Tests if two values are equal.
|
|
24
|
+
|
|
25
|
+
```xs
|
|
26
|
+
db.query "post" {
|
|
27
|
+
where = $db.post.user_id == $auth.id
|
|
28
|
+
} as $posts
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Tests if two values are not equal.
|
|
32
|
+
|
|
33
|
+
```xs
|
|
34
|
+
db.query "post" {
|
|
35
|
+
where = $db.post.status != "draft"
|
|
36
|
+
} as $published_posts
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
#### Array Content Operators
|
|
40
|
+
|
|
41
|
+
**`contains` (contains) and `not contains` (does not contain)**
|
|
42
|
+
Tests if an array contains a specific value.
|
|
43
|
+
|
|
44
|
+
IMPORTANT, DO NOT USE `icontains` IT IS NOT VALID IN A QUERY
|
|
45
|
+
use `contains` instead as it is case insensitive by default.
|
|
46
|
+
|
|
47
|
+
Tests if a field does not contain a specific value.
|
|
48
|
+
|
|
49
|
+
```xs
|
|
50
|
+
db.query "post" {
|
|
51
|
+
description = "Posts without specific tags"
|
|
52
|
+
where = $db.post.tags not contains "deprecated"
|
|
53
|
+
} as $current_posts
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
#### String Content Operators
|
|
57
|
+
|
|
58
|
+
**`includes` (includes)**
|
|
59
|
+
Tests if a string includes a particular phrase / value.
|
|
60
|
+
|
|
61
|
+
```xs
|
|
62
|
+
db.query "post" {
|
|
63
|
+
description = "Posts with specific title content"
|
|
64
|
+
where = $db.post.title includes "tutorial"
|
|
65
|
+
} as $tutorial_posts
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
#### Array Overlap Operators
|
|
69
|
+
|
|
70
|
+
**`overlaps` (overlaps) and `not overlaps` (does not overlap)**
|
|
71
|
+
Tests if two arrays have any elements in common. Useful for comparing array fields.
|
|
72
|
+
|
|
73
|
+
```xs
|
|
74
|
+
db.query "post" {
|
|
75
|
+
description = "Posts with overlapping tags"
|
|
76
|
+
where = $db.post.tags overlaps ["javascript", "react"]
|
|
77
|
+
} as $matching_posts
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Tests if two arrays have no elements in common.
|
|
81
|
+
|
|
82
|
+
```xs
|
|
83
|
+
db.query "post" {
|
|
84
|
+
description = "Posts without conflicting tags"
|
|
85
|
+
where = $db.post.tags not overlaps ["outdated", "deprecated"]
|
|
86
|
+
} as $valid_posts
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
#### Combining Conditions
|
|
90
|
+
|
|
91
|
+
You can combine multiple conditions using logical operators:
|
|
92
|
+
|
|
93
|
+
**`&&` (logical AND)**
|
|
94
|
+
|
|
95
|
+
```xs
|
|
96
|
+
db.query "post" {
|
|
97
|
+
where = $db.post.user_id == $auth.id && $db.post.status == "published"
|
|
98
|
+
} as $my_published_posts
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
**`||` (logical OR)**
|
|
102
|
+
|
|
103
|
+
```xs
|
|
104
|
+
db.query "post" {
|
|
105
|
+
where = $db.post.user_id == $auth.id || $db.post.status == "published"
|
|
106
|
+
} as $my_published_posts
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
#### Ignore if null Operators
|
|
110
|
+
|
|
111
|
+
When filtering data, some condition might be optional based on user input. In such cases, you can use the `?` operator after the comparison operator (`==` would become `==?`) to ignore that condition if the value is null. For example, if you have an optional `category` input parameter, you can write:
|
|
112
|
+
|
|
113
|
+
```xs
|
|
114
|
+
db.query "post" {
|
|
115
|
+
where = $db.post.category ==? $input.category
|
|
116
|
+
} as $filtered_posts
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
If you have a set of advanced search filters, you can use multiple ignore-if-null conditions:
|
|
120
|
+
|
|
121
|
+
```xs
|
|
122
|
+
db.query "post" {
|
|
123
|
+
where = $db.post.category ==? $input.category && $db.post.status ==? $input.status && $db.post.created_at >=? $input.start_date && $db.post.created_at <=? $input.end_date
|
|
124
|
+
} as $filtered_posts
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
This way, if any of the input parameters are null, that specific condition will be ignored in the query.
|
|
128
|
+
|
|
129
|
+
### join
|
|
130
|
+
|
|
131
|
+
binding allows you to join related tables in your query. You can specify the relationship using the `bind` argument, which takes an array of objects defining the join conditions.
|
|
132
|
+
|
|
133
|
+
```xs
|
|
134
|
+
db.query "comment" {
|
|
135
|
+
where = $db.post.user_id == $auth.id && $db.post.status == "published" && $db.post.created_at > ("now"|timestamp_add_days:-30)
|
|
136
|
+
join = {
|
|
137
|
+
post: {
|
|
138
|
+
table : "post"
|
|
139
|
+
type : "inner"
|
|
140
|
+
where: $db.comment.post_id == $db.post.id
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
} as $my_published_posts
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
The joins can be of type `inner`, `left`, or `right`, depending on your needs.
|
|
147
|
+
|
|
148
|
+
Note that joining a table does not return its fields; it only allows you to use those fields in the `search` condition. If you wanted to return fields from the joined table, you would want to either use an `eval` to map some of the values retrieved from the join or use an `addon` which would fetch the related data separately.
|
|
149
|
+
|
|
150
|
+
### Addon
|
|
151
|
+
|
|
152
|
+
An Addon is a function that is running a single `db.query` statement to fetch related data for each record returned by the main query. This is useful for fetching related records without using joins. Note that only a single `db.query` statement is allowed in an addon stack, no other operations is allowed.
|
|
153
|
+
|
|
154
|
+
For example, if you wanted to fetch blog posts along with their likes and comments, you could use addons like this (assuming you have `blog_post_likes` and `blog_post_comments` addons defined):
|
|
155
|
+
|
|
156
|
+
```xs
|
|
157
|
+
db.query blog_post {
|
|
158
|
+
where = $db.blog_post.author_id == $auth.id
|
|
159
|
+
sort = {blog_post.publication_date: "desc"}
|
|
160
|
+
return = {type: "list", paging: {page: 1, per_page: 25, totals: true}}
|
|
161
|
+
addon = [
|
|
162
|
+
{
|
|
163
|
+
name : "blog_post_like_count"
|
|
164
|
+
input: {blog_post_id: $output.id}
|
|
165
|
+
as : "items.like_count"
|
|
166
|
+
}
|
|
167
|
+
{
|
|
168
|
+
name : "blog_post_comment_count"
|
|
169
|
+
input: {blog_post_id: $output.id}
|
|
170
|
+
as : "items.comment_count"
|
|
171
|
+
}
|
|
172
|
+
]
|
|
173
|
+
} as $posts
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
The `blog_post_like_count` addon could be defined as:
|
|
177
|
+
|
|
178
|
+
```xs
|
|
179
|
+
addon blog_post_like_count {
|
|
180
|
+
input {
|
|
181
|
+
uuid blog_post_id? {
|
|
182
|
+
table = "blog_post"
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
stack {
|
|
187
|
+
db.query blog_post_like {
|
|
188
|
+
where = $db.blog_post_like.blog_post_id == $input.blog_post_id
|
|
189
|
+
return = {type: "count"}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
and the `blog_post_comment_count` addon could be defined as:
|
|
196
|
+
|
|
197
|
+
```xs
|
|
198
|
+
addon blog_post_comment_count {
|
|
199
|
+
input {
|
|
200
|
+
uuid blog_post_id? {
|
|
201
|
+
table = "blog_post"
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
stack {
|
|
206
|
+
db.query blog_post_comment {
|
|
207
|
+
where = $db.blog_post_comment.blog_post_id == $input.blog_post_id
|
|
208
|
+
return = {type: "count"}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
### Eval
|
|
215
|
+
|
|
216
|
+
Eval allows you to create computed fields based on existing fields in your database (or joined tables). You can define these computed fields using the `eval` argument, which takes an array of objects specifying the computation.
|
|
217
|
+
|
|
218
|
+
```xs
|
|
219
|
+
db.query "blog_post" {
|
|
220
|
+
join = {
|
|
221
|
+
user: {table: "user", where: $db.post.user_id == $db.user.id}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
where = $db.post.user_id == $auth.id
|
|
225
|
+
eval = {status: $db.user.status, userName: $db.user.name}
|
|
226
|
+
return = {type: "list"}
|
|
227
|
+
} as $posts
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
### Return types
|
|
231
|
+
|
|
232
|
+
- `count`: Returns the number of matching records.
|
|
233
|
+
- `exists`: Returns whether any matching records exist.
|
|
234
|
+
- `single`: Returns a single matching record.
|
|
235
|
+
- `list`: Returns a list of matching records.
|
|
236
|
+
|
|
237
|
+
#### Return List
|
|
238
|
+
|
|
239
|
+
This is the default format when none is specified. You can specify pagination, sorting, and whether to include metadata.
|
|
240
|
+
|
|
241
|
+
You can specify sorting :
|
|
242
|
+
|
|
243
|
+
```xs
|
|
244
|
+
db.query "blog_post_comment" {
|
|
245
|
+
where = $db.blog_post_comment.user_id == $auth.id && $db.blog_post_comment.status == "published"
|
|
246
|
+
sort = {post.created_at: "asc"}
|
|
247
|
+
return = {type: "list"}
|
|
248
|
+
} as $my_published_posts
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
sorting can be `asc`, `desc` or `rand` (random order).
|
|
252
|
+
|
|
253
|
+
You can also specify pagination (and combine it with sorting):
|
|
254
|
+
|
|
255
|
+
```xs
|
|
256
|
+
db.query "blog_post_comment" {
|
|
257
|
+
join = {
|
|
258
|
+
post: {
|
|
259
|
+
table : "blog_post"
|
|
260
|
+
where: $db.blog_post_comment.post_id == $db.blog_post.id
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
where = $db.blog_post_comment.user_id == $auth.id
|
|
265
|
+
additional_where = $input.query
|
|
266
|
+
sort = { blog_post.created_at: "asc" }
|
|
267
|
+
return = { type: "list", paging: { page: $input.page, per_page: 25 } }
|
|
268
|
+
} as $my_published_posts
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
paging defines the default constant you want to use for pagination, while external_simple allows you to override those values (here $input parameters).
|
|
272
|
+
|
|
273
|
+
This pagination will move the results into a new structure as:
|
|
274
|
+
|
|
275
|
+
```json
|
|
276
|
+
{
|
|
277
|
+
"itemsReceived": integer,
|
|
278
|
+
"curPage": integer,
|
|
279
|
+
"nextPage": integer,
|
|
280
|
+
"prevPage": integer,
|
|
281
|
+
"offset": integer,
|
|
282
|
+
"perPage": integer,
|
|
283
|
+
"items": [ ... ]
|
|
284
|
+
}
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
#### Return Single
|
|
288
|
+
|
|
289
|
+
Returns a single matching record. If multiple records match, only the first one is returned.
|
|
290
|
+
|
|
291
|
+
```xs
|
|
292
|
+
db.query "comment" {
|
|
293
|
+
where = $db.comment.user_id == $auth.id
|
|
294
|
+
return = {
|
|
295
|
+
type: "single"
|
|
296
|
+
}
|
|
297
|
+
} as $my_published_posts
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
#### Return Count
|
|
301
|
+
|
|
302
|
+
Returns the number of matching records as an integer.
|
|
303
|
+
|
|
304
|
+
```xs
|
|
305
|
+
db.query "comment" {
|
|
306
|
+
where = $db.comment.user_id == $auth.id
|
|
307
|
+
return = {
|
|
308
|
+
type: "count"
|
|
309
|
+
}
|
|
310
|
+
} as $post_count
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
#### Return Exists
|
|
314
|
+
|
|
315
|
+
Returns whether any matching records exist as a boolean.
|
|
316
|
+
|
|
317
|
+
```xs
|
|
318
|
+
db.query "comment" {
|
|
319
|
+
where = $db.comment.user_id == $auth.id
|
|
320
|
+
return = {
|
|
321
|
+
type: "exists"
|
|
322
|
+
}
|
|
323
|
+
} as $has_posts
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
### Other convenience query operations
|
|
327
|
+
|
|
328
|
+
#### db.get
|
|
329
|
+
|
|
330
|
+
Retrieving a single record by its primary key can be done using `db.get`:
|
|
331
|
+
|
|
332
|
+
```xs
|
|
333
|
+
db.get "follow" {
|
|
334
|
+
field_name = "id"
|
|
335
|
+
field_value = $input.follow_id
|
|
336
|
+
} as $follow_record
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
#### db.has
|
|
340
|
+
|
|
341
|
+
Checking for the existence of a record can be done using `db.has`:
|
|
342
|
+
|
|
343
|
+
```xs
|
|
344
|
+
db.has "category" {
|
|
345
|
+
field_name = "id"
|
|
346
|
+
field_value = $input.query
|
|
347
|
+
} as $category_exists
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
#### Modifying your table
|
|
351
|
+
|
|
352
|
+
Adding a new record can be done using `db.add`:
|
|
353
|
+
|
|
354
|
+
```xs
|
|
355
|
+
db.add "post" {
|
|
356
|
+
data = {
|
|
357
|
+
user_id : $auth.id
|
|
358
|
+
caption : $input.caption
|
|
359
|
+
image_url: $input.image_url
|
|
360
|
+
status : "draft"
|
|
361
|
+
}
|
|
362
|
+
} as $new_post
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
Editing a record can be done using `db.patch` this is preferred method to update a record and when the payload is built dynamically since the data object also accepts a variable.
|
|
366
|
+
|
|
367
|
+
```xs
|
|
368
|
+
var $payload {
|
|
369
|
+
value = {status: "active"}
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
conditional {
|
|
373
|
+
if ($input.is_featured) {
|
|
374
|
+
var.update $payload.featured {
|
|
375
|
+
value = true
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
else {
|
|
380
|
+
var.update $payload.featured {
|
|
381
|
+
value = false
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
db.patch cards {
|
|
387
|
+
field_name = "id"
|
|
388
|
+
field_value = $input.card_id
|
|
389
|
+
data = $payload
|
|
390
|
+
} as $cards1
|
|
391
|
+
```
|
|
392
|
+
|
|
393
|
+
Editing a record can also be done using `db.edit`. This method does not accept a variable for the data object and requires the fields to be updated to be specified in place.
|
|
394
|
+
|
|
395
|
+
```xs
|
|
396
|
+
db.edit "post" {
|
|
397
|
+
field_name = "id"
|
|
398
|
+
field_value = $input.post_id
|
|
399
|
+
data = {
|
|
400
|
+
caption : input.value
|
|
401
|
+
image_url: $input.image_url || $post.image_url
|
|
402
|
+
}
|
|
403
|
+
} as $edited_post
|
|
404
|
+
```
|
|
405
|
+
|
|
406
|
+
Adding or editing (upsert) a record can be done using `db.add_or_edit`. In this case, if the `field_value` is empty, a new record will be created; otherwise, the existing record will be updated.
|
|
407
|
+
|
|
408
|
+
```xs
|
|
409
|
+
db.add_or_edit "category" {
|
|
410
|
+
field_name = "name"
|
|
411
|
+
field_value = "$input.category"
|
|
412
|
+
data = {
|
|
413
|
+
name: $input.name
|
|
414
|
+
}
|
|
415
|
+
} as $category_record
|
|
416
|
+
```
|
|
417
|
+
|
|
418
|
+
#### db.del
|
|
419
|
+
|
|
420
|
+
Deleting a record can be done using `db.del` (notice how delete does not return the deleted record):
|
|
421
|
+
|
|
422
|
+
```xs
|
|
423
|
+
db.del "post" {
|
|
424
|
+
field_name = "id"
|
|
425
|
+
field_value = $input.post_id
|
|
426
|
+
}
|
|
427
|
+
```
|