@xano/developer-mcp 1.0.7 → 1.0.9

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.
@@ -0,0 +1,407 @@
1
+ ---
2
+ applyTo: "functions/**/*.xs, apis/**/*.xs"
3
+ ---
4
+
5
+ # Performance Optimization
6
+
7
+ Best practices for building fast, efficient XanoScript applications.
8
+
9
+ ## Quick Reference
10
+
11
+ | Area | Key Techniques |
12
+ |------|----------------|
13
+ | Database | Indexes, query optimization, pagination |
14
+ | Caching | Redis, response caching |
15
+ | Loops | Bulk operations, parallel execution |
16
+ | Filters | Efficient data transformations |
17
+
18
+ ---
19
+
20
+ ## Database Optimization
21
+
22
+ ### Use Indexes
23
+
24
+ Ensure frequently queried columns are indexed in your table definitions.
25
+
26
+ ```xs
27
+ table "order" {
28
+ int id { primary_key = true }
29
+ int user_id { index = true }
30
+ timestamp created_at { index = true }
31
+ text status { index = true }
32
+ // Compound index for common query pattern
33
+ index = [["user_id", "created_at"]]
34
+ }
35
+ ```
36
+
37
+ ### Query Only What You Need
38
+
39
+ ```xs
40
+ // Good: Select specific fields
41
+ db.query "user" {
42
+ where = $db.user.is_active == true
43
+ select = ["id", "name", "email"]
44
+ } as $users
45
+
46
+ // Avoid: Selecting all fields when only a few are needed
47
+ db.query "user" {
48
+ where = $db.user.is_active == true
49
+ } as $users // Fetches all columns
50
+ ```
51
+
52
+ ### Use Appropriate Return Types
53
+
54
+ ```xs
55
+ // Need count only? Use count return type
56
+ db.query "order" {
57
+ where = $db.order.user_id == $auth.id
58
+ return = { type: "count" }
59
+ } as $order_count
60
+
61
+ // Need existence check? Use exists return type
62
+ db.query "user" {
63
+ where = $db.user.email == $input.email
64
+ return = { type: "exists" }
65
+ } as $exists
66
+
67
+ // Don't fetch full records just to count them
68
+ ```
69
+
70
+ ### Pagination for Large Results
71
+
72
+ ```xs
73
+ // Always paginate large result sets
74
+ db.query "product" {
75
+ where = $db.product.is_active == true
76
+ return = {
77
+ type: "list",
78
+ paging: {
79
+ page: $input.page,
80
+ per_page: 25,
81
+ totals: true
82
+ }
83
+ }
84
+ sort = { created_at: "desc" }
85
+ } as $products
86
+ ```
87
+
88
+ ### Avoid N+1 Queries
89
+
90
+ ```xs
91
+ // Bad: Query inside loop (N+1)
92
+ foreach ($orders) {
93
+ each as $order {
94
+ db.get "user" {
95
+ field_name = "id"
96
+ field_value = $order.user_id
97
+ } as $user
98
+ }
99
+ }
100
+
101
+ // Good: Use join or batch fetch
102
+ db.query "order" {
103
+ join = {
104
+ user: {
105
+ table: "user",
106
+ where: $db.order.user_id == $db.user.id
107
+ }
108
+ }
109
+ eval = {
110
+ user_name: $db.user.name,
111
+ user_email: $db.user.email
112
+ }
113
+ } as $orders
114
+ ```
115
+
116
+ ### Use Bulk Operations
117
+
118
+ ```xs
119
+ // Bad: Individual inserts in loop
120
+ foreach ($items) {
121
+ each as $item {
122
+ db.add "order_item" { data = $item }
123
+ }
124
+ }
125
+
126
+ // Good: Bulk insert
127
+ db.bulk.add "order_item" {
128
+ data = $items
129
+ }
130
+ ```
131
+
132
+ ---
133
+
134
+ ## Caching Strategies
135
+
136
+ ### Redis Caching
137
+
138
+ ```xs
139
+ // Check cache first
140
+ redis.get { key = "user:" ~ $input.user_id } as $cached
141
+
142
+ conditional {
143
+ if ($cached != null) {
144
+ var $user { value = $cached }
145
+ }
146
+ else {
147
+ db.get "user" {
148
+ field_name = "id"
149
+ field_value = $input.user_id
150
+ } as $user
151
+
152
+ // Cache for 5 minutes
153
+ redis.set {
154
+ key = "user:" ~ $input.user_id
155
+ data = $user
156
+ ttl = 300
157
+ }
158
+ }
159
+ }
160
+ ```
161
+
162
+ ### Cache Invalidation
163
+
164
+ ```xs
165
+ function "update_user" {
166
+ input { int user_id, object data }
167
+ stack {
168
+ db.patch "user" {
169
+ field_name = "id"
170
+ field_value = $input.user_id
171
+ data = $input.data
172
+ } as $user
173
+
174
+ // Invalidate cache
175
+ redis.del { key = "user:" ~ $input.user_id }
176
+ }
177
+ response = $user
178
+ }
179
+ ```
180
+
181
+ ### Computed Value Caching
182
+
183
+ ```xs
184
+ // Cache expensive computations
185
+ redis.get { key = "stats:daily:" ~ now|format_timestamp:"Y-m-d" } as $cached_stats
186
+
187
+ conditional {
188
+ if ($cached_stats == null) {
189
+ // Expensive aggregation
190
+ db.query "order" {
191
+ where = $db.order.created_at >= now|transform_timestamp:"start of day"
192
+ } as $orders
193
+
194
+ var $stats {
195
+ value = {
196
+ count: $orders|count,
197
+ total: $orders|map:$$.total|sum,
198
+ avg: $orders|map:$$.total|avg
199
+ }
200
+ }
201
+
202
+ redis.set {
203
+ key = "stats:daily:" ~ now|format_timestamp:"Y-m-d"
204
+ data = $stats
205
+ ttl = 300
206
+ }
207
+
208
+ var $cached_stats { value = $stats }
209
+ }
210
+ }
211
+ ```
212
+
213
+ ---
214
+
215
+ ## Parallel Execution
216
+
217
+ ### Use Group for Independent Operations
218
+
219
+ ```xs
220
+ // Execute in parallel
221
+ group {
222
+ db.query "user" { return = { type: "count" } } as $user_count
223
+ db.query "order" { return = { type: "count" } } as $order_count
224
+ db.query "product" { return = { type: "count" } } as $product_count
225
+ }
226
+
227
+ // All counts available after group completes
228
+ response = {
229
+ users: $user_count,
230
+ orders: $order_count,
231
+ products: $product_count
232
+ }
233
+ ```
234
+
235
+ ### Parallel API Calls
236
+
237
+ ```xs
238
+ group {
239
+ api.request { url = "https://api1.example.com/data" } as $data1
240
+ api.request { url = "https://api2.example.com/data" } as $data2
241
+ api.request { url = "https://api3.example.com/data" } as $data3
242
+ }
243
+ ```
244
+
245
+ ---
246
+
247
+ ## Efficient Data Transformations
248
+
249
+ ### Use Filter Chains Wisely
250
+
251
+ ```xs
252
+ // Good: Single pass with chained filters
253
+ $data|filter:$$.active|map:$$.name|unique
254
+
255
+ // Avoid: Multiple passes over data
256
+ var $active { value = $data|filter:$$.active }
257
+ var $names { value = $active|map:$$.name }
258
+ var $unique { value = $names|unique }
259
+ ```
260
+
261
+ ### Early Filtering
262
+
263
+ ```xs
264
+ // Filter in database, not in code
265
+ db.query "product" {
266
+ where = $db.product.is_active == true && $db.product.price > 0
267
+ } as $products
268
+
269
+ // Not: Fetch all then filter
270
+ db.query "product" as $all_products
271
+ var $products { value = $all_products|filter:$$.is_active && $$.price > 0 }
272
+ ```
273
+
274
+ ### Limit Data Transfer
275
+
276
+ ```xs
277
+ // Only fetch needed fields from external API
278
+ api.request {
279
+ url = "https://api.example.com/users"
280
+ params = { fields: "id,name,email" }
281
+ } as $response
282
+ ```
283
+
284
+ ---
285
+
286
+ ## Rate Limiting
287
+
288
+ ### Protect Endpoints
289
+
290
+ ```xs
291
+ redis.ratelimit {
292
+ key = "api:" ~ $auth.id
293
+ max = 100
294
+ ttl = 60
295
+ error = "Rate limit exceeded. Try again in 1 minute."
296
+ }
297
+ ```
298
+
299
+ ### Tiered Limits
300
+
301
+ ```xs
302
+ // Different limits by user tier
303
+ var $limit {
304
+ value = conditional {
305
+ if ($auth.tier == "premium") { 1000 }
306
+ elseif ($auth.tier == "pro") { 500 }
307
+ else { 100 }
308
+ }
309
+ }
310
+
311
+ redis.ratelimit {
312
+ key = "api:" ~ $auth.id
313
+ max = $limit
314
+ ttl = 60
315
+ }
316
+ ```
317
+
318
+ ---
319
+
320
+ ## Response Optimization
321
+
322
+ ### Stream Large Responses
323
+
324
+ ```xs
325
+ // Stream instead of loading into memory
326
+ db.query "log" {
327
+ where = $db.log.created_at >= $input.start
328
+ return = { type: "stream" }
329
+ } as $logs
330
+
331
+ api.stream {
332
+ format = "jsonl"
333
+ value = $logs
334
+ }
335
+ ```
336
+
337
+ ### Compress Responses
338
+
339
+ Large JSON responses are automatically compressed when clients support it.
340
+
341
+ ### Paginate API Responses
342
+
343
+ ```xs
344
+ query "list_products" {
345
+ input {
346
+ int page?=1
347
+ int per_page?=25 filters=max:100
348
+ }
349
+ stack {
350
+ db.query "product" {
351
+ return = {
352
+ type: "list",
353
+ paging: {
354
+ page: $input.page,
355
+ per_page: $input.per_page,
356
+ totals: true
357
+ }
358
+ }
359
+ } as $products
360
+ }
361
+ response = $products
362
+ }
363
+ ```
364
+
365
+ ---
366
+
367
+ ## Query Analysis
368
+
369
+ ### Log Slow Queries
370
+
371
+ ```xs
372
+ var $start { value = now|to_ms }
373
+
374
+ db.query "product" {
375
+ where = $db.product.category == $input.category
376
+ } as $products
377
+
378
+ var $duration { value = (now|to_ms) - $start }
379
+
380
+ conditional {
381
+ if ($duration > 100) {
382
+ debug.log {
383
+ label = "SLOW_QUERY"
384
+ value = {
385
+ query: "product by category",
386
+ duration_ms: $duration,
387
+ result_count: $products|count
388
+ }
389
+ }
390
+ }
391
+ }
392
+ ```
393
+
394
+ ---
395
+
396
+ ## Best Practices Summary
397
+
398
+ 1. **Index frequently queried columns** - Check query patterns
399
+ 2. **Use appropriate return types** - count, exists, single
400
+ 3. **Paginate large results** - Never return unbounded lists
401
+ 4. **Avoid N+1 queries** - Use joins or batch fetching
402
+ 5. **Use bulk operations** - For batch inserts/updates
403
+ 6. **Cache expensive operations** - Redis with appropriate TTL
404
+ 7. **Execute in parallel** - Group independent operations
405
+ 8. **Filter early** - In database, not application code
406
+ 9. **Stream large responses** - Don't load into memory
407
+ 10. **Monitor performance** - Log slow operations