@xano/developer-mcp 1.0.1 → 1.0.2
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 +96 -31
- package/dist/index.js +248 -180
- package/package.json +4 -2
- package/xanoscript_docs/README.md +107 -1
- package/xanoscript_docs/agents.md +329 -0
- package/xanoscript_docs/apis.md +343 -0
- package/xanoscript_docs/database.md +417 -0
- package/xanoscript_docs/ephemeral.md +333 -0
- package/xanoscript_docs/frontend.md +291 -0
- package/xanoscript_docs/functions.md +232 -2035
- package/xanoscript_docs/integrations.md +439 -0
- package/xanoscript_docs/mcp-servers.md +190 -0
- package/xanoscript_docs/plan.md +192 -0
- package/xanoscript_docs/syntax.md +314 -0
- package/xanoscript_docs/tables.md +270 -0
- package/xanoscript_docs/tasks.md +254 -0
- package/xanoscript_docs/testing.md +335 -0
- package/xanoscript_docs/tools.md +305 -0
- package/xanoscript_docs/types.md +297 -0
- package/xanoscript_docs/version.json +2 -1
- package/xanoscript_docs/api_query_examples.md +0 -1255
- package/xanoscript_docs/api_query_guideline.md +0 -129
- package/xanoscript_docs/build_from_lovable.md +0 -715
- package/xanoscript_docs/db_query_guideline.md +0 -427
- package/xanoscript_docs/ephemeral_environment_guideline.md +0 -529
- package/xanoscript_docs/expression_guideline.md +0 -1086
- package/xanoscript_docs/frontend_guideline.md +0 -67
- package/xanoscript_docs/function_examples.md +0 -1406
- package/xanoscript_docs/function_guideline.md +0 -130
- package/xanoscript_docs/input_guideline.md +0 -227
- package/xanoscript_docs/mcp_server_examples.md +0 -36
- package/xanoscript_docs/mcp_server_guideline.md +0 -69
- package/xanoscript_docs/query_filter.md +0 -489
- package/xanoscript_docs/table_examples.md +0 -586
- package/xanoscript_docs/table_guideline.md +0 -137
- package/xanoscript_docs/task_examples.md +0 -511
- package/xanoscript_docs/task_guideline.md +0 -103
- package/xanoscript_docs/tips_and_tricks.md +0 -144
- package/xanoscript_docs/tool_examples.md +0 -69
- package/xanoscript_docs/tool_guideline.md +0 -139
- package/xanoscript_docs/unit_testing_guideline.md +0 -328
- package/xanoscript_docs/workspace.md +0 -17
|
@@ -0,0 +1,333 @@
|
|
|
1
|
+
---
|
|
2
|
+
applyTo: "ephemeral/**/*.xs"
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Ephemeral Environments
|
|
6
|
+
|
|
7
|
+
Temporary Xano workspaces for testing and one-off operations.
|
|
8
|
+
|
|
9
|
+
## Quick Reference
|
|
10
|
+
|
|
11
|
+
| Type | Purpose | Lifecycle |
|
|
12
|
+
|------|---------|-----------|
|
|
13
|
+
| **Service** | Temporary workspace with DB/APIs | Runs until shut down |
|
|
14
|
+
| **Job** | One-time operation | Executes once, then shuts down |
|
|
15
|
+
|
|
16
|
+
### Directory Structure
|
|
17
|
+
```
|
|
18
|
+
ephemeral/
|
|
19
|
+
├── my-service/
|
|
20
|
+
│ ├── workspace.xs
|
|
21
|
+
│ ├── tables/
|
|
22
|
+
│ ├── functions/
|
|
23
|
+
│ └── apis/
|
|
24
|
+
└── my-job/
|
|
25
|
+
├── workspace.xs
|
|
26
|
+
├── tables/
|
|
27
|
+
└── functions/
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## Ephemeral Service
|
|
33
|
+
|
|
34
|
+
A temporary workspace with database, functions, and API endpoints.
|
|
35
|
+
|
|
36
|
+
### workspace.xs
|
|
37
|
+
```xs
|
|
38
|
+
workspace my_service {
|
|
39
|
+
env = {
|
|
40
|
+
api_key: "test-key"
|
|
41
|
+
debug: "true"
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### Table with Seed Data
|
|
47
|
+
```xs
|
|
48
|
+
table event {
|
|
49
|
+
auth = false
|
|
50
|
+
schema {
|
|
51
|
+
int id
|
|
52
|
+
timestamp created_at?=now
|
|
53
|
+
text name filters=trim
|
|
54
|
+
}
|
|
55
|
+
index = [
|
|
56
|
+
{type: "primary", field: [{name: "id"}]}
|
|
57
|
+
]
|
|
58
|
+
items = [
|
|
59
|
+
{"id": 1, "name": "Event 1"}
|
|
60
|
+
{"id": 2, "name": "Event 2"}
|
|
61
|
+
]
|
|
62
|
+
}
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### API Group
|
|
66
|
+
```xs
|
|
67
|
+
api_group events {
|
|
68
|
+
canonical = "events-api"
|
|
69
|
+
}
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### Endpoints
|
|
73
|
+
```xs
|
|
74
|
+
query list verb=GET {
|
|
75
|
+
api_group = "events"
|
|
76
|
+
stack {
|
|
77
|
+
db.query event {
|
|
78
|
+
return = { type: "list" }
|
|
79
|
+
} as $events
|
|
80
|
+
}
|
|
81
|
+
response = $events
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
query add verb=POST {
|
|
85
|
+
api_group = "events"
|
|
86
|
+
input { text name filters=trim }
|
|
87
|
+
stack {
|
|
88
|
+
db.add event {
|
|
89
|
+
data = { name: $input.name }
|
|
90
|
+
} as $event
|
|
91
|
+
}
|
|
92
|
+
response = $event
|
|
93
|
+
}
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
---
|
|
97
|
+
|
|
98
|
+
## Ephemeral Job
|
|
99
|
+
|
|
100
|
+
One-time operation with setup and cleanup hooks.
|
|
101
|
+
|
|
102
|
+
### Reserved Functions
|
|
103
|
+
| Function | Purpose | Required |
|
|
104
|
+
|----------|---------|----------|
|
|
105
|
+
| `$main` | Primary logic | Yes |
|
|
106
|
+
| `$pre` | Setup/validation | No |
|
|
107
|
+
| `$post` | Cleanup/notification | No |
|
|
108
|
+
|
|
109
|
+
### Execution Order
|
|
110
|
+
1. `$pre` (if defined)
|
|
111
|
+
2. `$main`
|
|
112
|
+
3. `$post` (if defined)
|
|
113
|
+
4. Environment shuts down
|
|
114
|
+
|
|
115
|
+
### $main Function
|
|
116
|
+
```xs
|
|
117
|
+
function "$main" {
|
|
118
|
+
input {
|
|
119
|
+
json args # Runtime arguments
|
|
120
|
+
json pre # Result from $pre
|
|
121
|
+
}
|
|
122
|
+
stack {
|
|
123
|
+
db.query authors {
|
|
124
|
+
return = { type: "count" }
|
|
125
|
+
} as $count
|
|
126
|
+
|
|
127
|
+
precondition ($count > 0) {
|
|
128
|
+
error = "No authors found"
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
db.add authors {
|
|
132
|
+
data = { name: "New Author" }
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
response = { processed: true }
|
|
136
|
+
}
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
### $pre Function (optional)
|
|
140
|
+
```xs
|
|
141
|
+
function "$pre" {
|
|
142
|
+
input { json args }
|
|
143
|
+
stack {
|
|
144
|
+
// Validation or setup
|
|
145
|
+
precondition ($input.args.required_field != null) {
|
|
146
|
+
error = "Missing required field"
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
response = { validated: true }
|
|
150
|
+
}
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
### $post Function (optional)
|
|
154
|
+
```xs
|
|
155
|
+
function "$post" {
|
|
156
|
+
input {
|
|
157
|
+
json args
|
|
158
|
+
json pre
|
|
159
|
+
json main
|
|
160
|
+
}
|
|
161
|
+
stack {
|
|
162
|
+
// Send notification or cleanup
|
|
163
|
+
api.request {
|
|
164
|
+
url = $env.WEBHOOK_URL
|
|
165
|
+
method = "POST"
|
|
166
|
+
params = {
|
|
167
|
+
status: "complete",
|
|
168
|
+
result: $input.main
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
response = null
|
|
173
|
+
}
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
---
|
|
177
|
+
|
|
178
|
+
## Complete Service Example
|
|
179
|
+
|
|
180
|
+
```
|
|
181
|
+
ephemeral/event-tracker/
|
|
182
|
+
├── workspace.xs
|
|
183
|
+
├── tables/
|
|
184
|
+
│ └── event.xs
|
|
185
|
+
└── apis/
|
|
186
|
+
├── api_group.xs
|
|
187
|
+
├── list.xs
|
|
188
|
+
├── add.xs
|
|
189
|
+
└── clear.xs
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
### workspace.xs
|
|
193
|
+
```xs
|
|
194
|
+
workspace event_tracker {
|
|
195
|
+
env = { api_key: "test" }
|
|
196
|
+
}
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
### tables/event.xs
|
|
200
|
+
```xs
|
|
201
|
+
table event {
|
|
202
|
+
auth = false
|
|
203
|
+
schema {
|
|
204
|
+
int id
|
|
205
|
+
timestamp created_at?=now
|
|
206
|
+
text name
|
|
207
|
+
}
|
|
208
|
+
index = [
|
|
209
|
+
{type: "primary", field: [{name: "id"}]}
|
|
210
|
+
]
|
|
211
|
+
items = []
|
|
212
|
+
}
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
### apis/list.xs
|
|
216
|
+
```xs
|
|
217
|
+
query list verb=GET {
|
|
218
|
+
api_group = "events"
|
|
219
|
+
stack {
|
|
220
|
+
db.query event {
|
|
221
|
+
sort = { created_at: "desc" }
|
|
222
|
+
return = { type: "list" }
|
|
223
|
+
} as $events
|
|
224
|
+
}
|
|
225
|
+
response = $events
|
|
226
|
+
}
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
---
|
|
230
|
+
|
|
231
|
+
## Complete Job Example
|
|
232
|
+
|
|
233
|
+
```
|
|
234
|
+
ephemeral/data-migration/
|
|
235
|
+
├── workspace.xs
|
|
236
|
+
├── tables/
|
|
237
|
+
│ └── users.xs
|
|
238
|
+
└── functions/
|
|
239
|
+
├── $main.xs
|
|
240
|
+
└── $post.xs
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
### workspace.xs
|
|
244
|
+
```xs
|
|
245
|
+
workspace data_migration {
|
|
246
|
+
env = { webhook_url: "https://webhook.site/xxx" }
|
|
247
|
+
}
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
### tables/users.xs
|
|
251
|
+
```xs
|
|
252
|
+
table users {
|
|
253
|
+
auth = false
|
|
254
|
+
schema {
|
|
255
|
+
int id
|
|
256
|
+
text name
|
|
257
|
+
text email
|
|
258
|
+
timestamp migrated_at?
|
|
259
|
+
}
|
|
260
|
+
index = [
|
|
261
|
+
{type: "primary", field: [{name: "id"}]}
|
|
262
|
+
]
|
|
263
|
+
items = [
|
|
264
|
+
{"id": 1, "name": "Alice", "email": "alice@example.com"}
|
|
265
|
+
{"id": 2, "name": "Bob", "email": "bob@example.com"}
|
|
266
|
+
]
|
|
267
|
+
}
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
### functions/$main.xs
|
|
271
|
+
```xs
|
|
272
|
+
function "$main" {
|
|
273
|
+
input { json args, json pre }
|
|
274
|
+
stack {
|
|
275
|
+
db.query users {
|
|
276
|
+
where = $db.users.migrated_at == null
|
|
277
|
+
} as $pending
|
|
278
|
+
|
|
279
|
+
foreach ($pending) {
|
|
280
|
+
each as $user {
|
|
281
|
+
db.edit users {
|
|
282
|
+
field_name = "id"
|
|
283
|
+
field_value = $user.id
|
|
284
|
+
data = { migrated_at: now }
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
response = { migrated: $pending|count }
|
|
290
|
+
}
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
### functions/$post.xs
|
|
294
|
+
```xs
|
|
295
|
+
function "$post" {
|
|
296
|
+
input { json args, json pre, json main }
|
|
297
|
+
stack {
|
|
298
|
+
api.request {
|
|
299
|
+
url = $env.webhook_url
|
|
300
|
+
method = "POST"
|
|
301
|
+
params = { result: $input.main }
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
response = null
|
|
305
|
+
}
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
---
|
|
309
|
+
|
|
310
|
+
## Deploying
|
|
311
|
+
|
|
312
|
+
### Deploy Service
|
|
313
|
+
```
|
|
314
|
+
publish_ephemeral_service name="my-api" directory="ephemeral/my-service" mode="service"
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
### Run Job
|
|
318
|
+
```
|
|
319
|
+
publish_ephemeral_service name="migration" directory="ephemeral/my-job" mode="job"
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
---
|
|
323
|
+
|
|
324
|
+
## Best Practices
|
|
325
|
+
|
|
326
|
+
1. **Keep isolated** - Self-contained with own tables/functions
|
|
327
|
+
2. **Seed test data** - Use `items` in table definitions
|
|
328
|
+
3. **Use environment variables** - Store config in workspace `env`
|
|
329
|
+
4. **Handle cleanup** - Use `$post` for notifications/cleanup
|
|
330
|
+
5. **Validate in $pre** - Check preconditions before main logic
|
|
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
|
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
---
|
|
2
|
+
applyTo: "static/**/*"
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Frontend
|
|
6
|
+
|
|
7
|
+
Static frontend development and Lovable/Supabase migration.
|
|
8
|
+
|
|
9
|
+
## Quick Reference
|
|
10
|
+
|
|
11
|
+
### Directory Structure
|
|
12
|
+
```
|
|
13
|
+
static/
|
|
14
|
+
├── index.html # Main entry point
|
|
15
|
+
├── css/
|
|
16
|
+
├── js/
|
|
17
|
+
│ └── api.js # Centralized API calls
|
|
18
|
+
└── assets/
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
### Key Tools
|
|
22
|
+
| Tool | Purpose |
|
|
23
|
+
|------|---------|
|
|
24
|
+
| `get_xano_api_specifications` | Get OpenAPI specs before frontend work |
|
|
25
|
+
| `upload_static_files_to_xano` | Deploy to Xano CDN |
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## Base Template
|
|
30
|
+
|
|
31
|
+
```html
|
|
32
|
+
<!DOCTYPE html>
|
|
33
|
+
<html lang="en">
|
|
34
|
+
<head>
|
|
35
|
+
<meta charset="utf-8">
|
|
36
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
37
|
+
<title>My App</title>
|
|
38
|
+
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.8/dist/css/bootstrap.min.css" rel="stylesheet">
|
|
39
|
+
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
|
|
40
|
+
</head>
|
|
41
|
+
<body>
|
|
42
|
+
<div id="app"></div>
|
|
43
|
+
<script src="js/api.js"></script>
|
|
44
|
+
<script src="js/app.js"></script>
|
|
45
|
+
</body>
|
|
46
|
+
</html>
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
## API Client
|
|
52
|
+
|
|
53
|
+
### Basic Setup (api.js)
|
|
54
|
+
```javascript
|
|
55
|
+
const API_BASE = 'https://your-instance.xano.io/api:group';
|
|
56
|
+
|
|
57
|
+
async function apiCall(endpoint, options = {}) {
|
|
58
|
+
const token = localStorage.getItem('auth_token');
|
|
59
|
+
const headers = {
|
|
60
|
+
'Content-Type': 'application/json',
|
|
61
|
+
...(token && { 'Authorization': `Bearer ${token}` })
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
const response = await fetch(`${API_BASE}${endpoint}`, {
|
|
65
|
+
...options,
|
|
66
|
+
headers: { ...headers, ...options.headers }
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
if (!response.ok) {
|
|
70
|
+
throw new Error(`API Error: ${response.status}`);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return response.json();
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// CRUD helpers
|
|
77
|
+
const api = {
|
|
78
|
+
get: (endpoint) => apiCall(endpoint),
|
|
79
|
+
post: (endpoint, data) => apiCall(endpoint, {
|
|
80
|
+
method: 'POST',
|
|
81
|
+
body: JSON.stringify(data)
|
|
82
|
+
}),
|
|
83
|
+
patch: (endpoint, data) => apiCall(endpoint, {
|
|
84
|
+
method: 'PATCH',
|
|
85
|
+
body: JSON.stringify(data)
|
|
86
|
+
}),
|
|
87
|
+
delete: (endpoint) => apiCall(endpoint, { method: 'DELETE' })
|
|
88
|
+
};
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
---
|
|
92
|
+
|
|
93
|
+
## Authentication
|
|
94
|
+
|
|
95
|
+
### Login
|
|
96
|
+
```javascript
|
|
97
|
+
async function login(email, password) {
|
|
98
|
+
const { authToken } = await api.post('/auth/login', { email, password });
|
|
99
|
+
localStorage.setItem('auth_token', authToken);
|
|
100
|
+
return authToken;
|
|
101
|
+
}
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### Signup
|
|
105
|
+
```javascript
|
|
106
|
+
async function signup(email, password, name) {
|
|
107
|
+
const { authToken } = await api.post('/auth/signup', {
|
|
108
|
+
email, password, name
|
|
109
|
+
});
|
|
110
|
+
localStorage.setItem('auth_token', authToken);
|
|
111
|
+
return authToken;
|
|
112
|
+
}
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### Logout
|
|
116
|
+
```javascript
|
|
117
|
+
function logout() {
|
|
118
|
+
localStorage.removeItem('auth_token');
|
|
119
|
+
window.location.href = '/login.html';
|
|
120
|
+
}
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### Get Current User
|
|
124
|
+
```javascript
|
|
125
|
+
async function getCurrentUser() {
|
|
126
|
+
return api.get('/auth/me');
|
|
127
|
+
}
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
---
|
|
131
|
+
|
|
132
|
+
## Lovable Migration
|
|
133
|
+
|
|
134
|
+
Migrate from Supabase to Xano using the compatibility SDK.
|
|
135
|
+
|
|
136
|
+
### 1. Install SDK
|
|
137
|
+
```bash
|
|
138
|
+
npm install @xano/supabase-compat
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### 2. Create Xano Client
|
|
142
|
+
```typescript
|
|
143
|
+
// src/integrations/xano/client.ts
|
|
144
|
+
import { createClient } from '@xano/supabase-compat';
|
|
145
|
+
|
|
146
|
+
export const xano = createClient(
|
|
147
|
+
import.meta.env.VITE_XANO_URL,
|
|
148
|
+
import.meta.env.VITE_XANO_AUTH_ENDPOINT,
|
|
149
|
+
{
|
|
150
|
+
auth: {
|
|
151
|
+
storage: localStorage,
|
|
152
|
+
persistSession: true,
|
|
153
|
+
autoRefreshToken: true
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
);
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
### 3. Update Environment
|
|
160
|
+
```env
|
|
161
|
+
VITE_XANO_URL=https://your-instance.xano.io
|
|
162
|
+
VITE_XANO_AUTH_ENDPOINT=/api:groupId/auth
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
### 4. Update Auth Hooks
|
|
166
|
+
|
|
167
|
+
Before (Supabase):
|
|
168
|
+
```typescript
|
|
169
|
+
import { supabase } from '@/integrations/supabase/client';
|
|
170
|
+
|
|
171
|
+
supabase.auth.signInWithPassword({ email, password });
|
|
172
|
+
supabase.auth.signUp({ email, password });
|
|
173
|
+
supabase.auth.signOut();
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
After (Xano):
|
|
177
|
+
```typescript
|
|
178
|
+
import { xano } from '@/integrations/xano/client';
|
|
179
|
+
|
|
180
|
+
xano.auth.signInWithPassword({ email, password });
|
|
181
|
+
xano.auth.signUp({ email, password });
|
|
182
|
+
xano.auth.signOut();
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
### 5. Update Data Hooks
|
|
186
|
+
|
|
187
|
+
Before (Supabase):
|
|
188
|
+
```typescript
|
|
189
|
+
const { data, error } = await supabase
|
|
190
|
+
.from('products')
|
|
191
|
+
.select('*')
|
|
192
|
+
.eq('category', 'electronics')
|
|
193
|
+
.order('created_at', { ascending: false });
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
After (Xano):
|
|
197
|
+
```typescript
|
|
198
|
+
const { data, error } = await xano
|
|
199
|
+
.endpoint('/api:group/products')
|
|
200
|
+
.get({
|
|
201
|
+
category: 'electronics',
|
|
202
|
+
sort: 'created_at',
|
|
203
|
+
order: 'desc'
|
|
204
|
+
});
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
---
|
|
208
|
+
|
|
209
|
+
## Common Patterns
|
|
210
|
+
|
|
211
|
+
### Pagination
|
|
212
|
+
```javascript
|
|
213
|
+
async function getProducts(page = 1, perPage = 20) {
|
|
214
|
+
return api.get(`/products?page=${page}&per_page=${perPage}`);
|
|
215
|
+
}
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
### Filtering
|
|
219
|
+
```javascript
|
|
220
|
+
async function searchProducts(query, category) {
|
|
221
|
+
const params = new URLSearchParams();
|
|
222
|
+
if (query) params.append('query', query);
|
|
223
|
+
if (category) params.append('category', category);
|
|
224
|
+
return api.get(`/products?${params}`);
|
|
225
|
+
}
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
### File Upload
|
|
229
|
+
```javascript
|
|
230
|
+
async function uploadImage(file) {
|
|
231
|
+
const formData = new FormData();
|
|
232
|
+
formData.append('image', file);
|
|
233
|
+
|
|
234
|
+
const response = await fetch(`${API_BASE}/upload`, {
|
|
235
|
+
method: 'POST',
|
|
236
|
+
headers: {
|
|
237
|
+
'Authorization': `Bearer ${localStorage.getItem('auth_token')}`
|
|
238
|
+
},
|
|
239
|
+
body: formData
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
return response.json();
|
|
243
|
+
}
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
---
|
|
247
|
+
|
|
248
|
+
## Migration Checklist
|
|
249
|
+
|
|
250
|
+
### Backend (Xano)
|
|
251
|
+
- [ ] Create tables matching Supabase schema
|
|
252
|
+
- [ ] Create CRUD endpoints for each resource
|
|
253
|
+
- [ ] Set up authentication endpoints
|
|
254
|
+
- [ ] Push changes: `push_all_changes_to_xano`
|
|
255
|
+
- [ ] Get API specs: `get_xano_api_specifications`
|
|
256
|
+
|
|
257
|
+
### Frontend
|
|
258
|
+
- [ ] Install `@xano/supabase-compat`
|
|
259
|
+
- [ ] Create Xano client
|
|
260
|
+
- [ ] Update environment variables
|
|
261
|
+
- [ ] Update auth hooks (minimal changes)
|
|
262
|
+
- [ ] Update data fetching (table → endpoint)
|
|
263
|
+
- [ ] Test all operations
|
|
264
|
+
- [ ] Remove Supabase dependencies
|
|
265
|
+
|
|
266
|
+
---
|
|
267
|
+
|
|
268
|
+
## Deployment
|
|
269
|
+
|
|
270
|
+
Deploy static files to Xano CDN:
|
|
271
|
+
|
|
272
|
+
```
|
|
273
|
+
invoke upload_static_files_to_xano
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
The tool will:
|
|
277
|
+
1. Bundle files from `static/`
|
|
278
|
+
2. Upload to Xano CDN
|
|
279
|
+
3. Return hosted URL
|
|
280
|
+
|
|
281
|
+
---
|
|
282
|
+
|
|
283
|
+
## Best Practices
|
|
284
|
+
|
|
285
|
+
1. **Get API specs first** - Use `get_xano_api_specifications` before coding
|
|
286
|
+
2. **Centralize API calls** - Single `api.js` file
|
|
287
|
+
3. **Handle errors** - Show user-friendly messages
|
|
288
|
+
4. **Store tokens securely** - localStorage for web, secure storage for mobile
|
|
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
|