Initial commit
This commit is contained in:
12
.claude-plugin/plugin.json
Normal file
12
.claude-plugin/plugin.json
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"name": "xano-backend-builder",
|
||||
"description": "Build and manage no-code backend services with Xano using MCP server integration. Create database tables, API endpoints, custom functions, and business logic using XanoScript. Use when building backend APIs, database schemas, serverless functions, webhooks, or integrating with Xano workspace.",
|
||||
"version": "1.0.0",
|
||||
"author": {
|
||||
"name": "Don Jacobsmeyer",
|
||||
"email": "hello@donjacobsmeyer.com"
|
||||
},
|
||||
"skills": [
|
||||
"./"
|
||||
]
|
||||
}
|
||||
14
.env.example
Normal file
14
.env.example
Normal file
@@ -0,0 +1,14 @@
|
||||
# Xano MCP Configuration
|
||||
# Copy this file to your shell profile (~/.zshrc or ~/.bashrc) and fill in your values
|
||||
|
||||
# Your Xano MCP server URL (found in Xano Settings → Metadata API & MCP Server)
|
||||
export XANO_MCP_URL="https://your-workspace.n7.xano.io/x2/mcp/meta/mcp/sse"
|
||||
|
||||
# Your Xano MCP access token (generate in Xano Settings → Metadata API & MCP Server)
|
||||
export XANO_MCP_TOKEN="your_xano_mcp_token_here"
|
||||
|
||||
# IMPORTANT:
|
||||
# 1. DO NOT commit actual tokens to git
|
||||
# 2. Add these exports to your shell profile (~/.zshrc or ~/.bashrc)
|
||||
# 3. Restart Claude Code after setting environment variables
|
||||
# 4. Verify with: echo $XANO_MCP_TOKEN
|
||||
11
.mcp.json
Normal file
11
.mcp.json
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"mcpServers": {
|
||||
"xano": {
|
||||
"type": "http",
|
||||
"url": "${XANO_MCP_URL}",
|
||||
"headers": {
|
||||
"Authorization": "Bearer ${XANO_MCP_TOKEN}"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
3
README.md
Normal file
3
README.md
Normal file
@@ -0,0 +1,3 @@
|
||||
# xano-backend-builder
|
||||
|
||||
Build and manage no-code backend services with Xano using MCP server integration. Create database tables, API endpoints, custom functions, and business logic using XanoScript. Use when building backend APIs, database schemas, serverless functions, webhooks, or integrating with Xano workspace.
|
||||
483
SKILL.md
Normal file
483
SKILL.md
Normal file
@@ -0,0 +1,483 @@
|
||||
---
|
||||
name: Xano Backend Builder
|
||||
description: Build and manage no-code backend services with Xano using MCP server integration. Create database tables, API endpoints, custom functions, and business logic using XanoScript. Use when building backend APIs, database schemas, serverless functions, webhooks, or integrating with Xano workspace.
|
||||
allowed-tools: Read, WebFetch, WebSearch, AskUserQuestion
|
||||
---
|
||||
|
||||
# Xano Backend Builder
|
||||
|
||||
Build serverless backend infrastructure using Xano's no-code platform through MCP (Model Context Protocol) server integration. Create databases, REST APIs, custom functions, and business logic using XanoScript without traditional coding.
|
||||
|
||||
## Instructions
|
||||
|
||||
### Prerequisites
|
||||
|
||||
1. **Xano Account and Workspace**:
|
||||
- Sign up at https://xano.com
|
||||
- Create or access an existing workspace
|
||||
|
||||
2. **MCP Access Token and URL**:
|
||||
- Navigate to workspace Settings → Metadata API & MCP Server
|
||||
- Copy your **MCP Server URL** (e.g., `https://x8ki-letl-twmt.n7.xano.io/mcp`)
|
||||
- Generate an **Access Token** with appropriate scopes
|
||||
- **CRITICAL**: Token appears only once - save it securely
|
||||
- **WARNING**: MCP operations can modify/delete data - use backups for production
|
||||
|
||||
3. **MCP Server Configuration**:
|
||||
This plugin includes MCP server configuration that connects automatically when enabled.
|
||||
|
||||
**Set environment variables**:
|
||||
```bash
|
||||
export XANO_MCP_URL="https://your-workspace.xano.io/mcp"
|
||||
export XANO_MCP_TOKEN="your_access_token_here"
|
||||
```
|
||||
|
||||
Or add to your shell profile (`~/.zshrc`, `~/.bashrc`):
|
||||
```bash
|
||||
# Xano MCP Configuration
|
||||
export XANO_MCP_URL="https://your-workspace.xano.io/mcp"
|
||||
export XANO_MCP_TOKEN="your_access_token_here"
|
||||
```
|
||||
|
||||
**Restart Claude Code** after setting environment variables.
|
||||
|
||||
The Xano MCP server will start automatically when this plugin is enabled, and Xano tools will be available in your Claude Code session.
|
||||
|
||||
### Understanding Xano Architecture
|
||||
|
||||
**Xano Components**:
|
||||
- **Database Tables**: Store structured data with relationships
|
||||
- **API Endpoints**: REST endpoints for CRUD operations and custom logic
|
||||
- **Function Stack**: Visual programming interface using XanoScript
|
||||
- **Background Tasks**: Scheduled or triggered async operations
|
||||
- **AI Agents**: Custom AI-powered functions and workflows
|
||||
|
||||
**XanoScript Basics**:
|
||||
Xano uses a proprietary syntax called XanoScript for defining logic. Key characteristics:
|
||||
- **Namespace functions**: `db.query`, `array.push`, `var.update`
|
||||
- **Variable assignment**: Use `as $variable_name`
|
||||
- **Filters**: Transform data with pipe syntax `|filter_name:option`
|
||||
- **Primitives**: Top-level constructs (API, function, table, etc.)
|
||||
|
||||
**Important**: XanoScript syntax is unique to Xano. When working with XanoScript:
|
||||
- Look for examples in Xano documentation or API responses
|
||||
- Test syntax incrementally
|
||||
- Reference [docs/xanoscript-reference.md](docs/xanoscript-reference.md) for detailed syntax guide
|
||||
|
||||
### Workflow
|
||||
|
||||
1. **Understand the requirement**:
|
||||
- What data needs to be stored? (database design)
|
||||
- What API endpoints are needed? (GET, POST, PUT, DELETE)
|
||||
- What business logic is required? (transformations, validations, external API calls)
|
||||
- What integrations are needed? (webhooks, OAuth, third-party APIs)
|
||||
|
||||
2. **Design the database schema**:
|
||||
- Identify entities (tables) and relationships
|
||||
- Define fields with appropriate data types
|
||||
- Consider indexes for performance
|
||||
- Use MCP tools to create tables:
|
||||
```
|
||||
Use Xano MCP tools to create a table with specified fields
|
||||
```
|
||||
|
||||
3. **Create API endpoints**:
|
||||
- Define REST endpoints (GET, POST, PUT, DELETE)
|
||||
- Configure authentication requirements
|
||||
- Set up input parameters and validation
|
||||
- Build response structure
|
||||
- Use MCP tools to create API endpoints
|
||||
|
||||
4. **Implement business logic with XanoScript**:
|
||||
- Use the Function Stack to build logic
|
||||
- **Key XanoScript patterns**:
|
||||
- Query database: `db.query table_name { filters } as $results`
|
||||
- Create variable: `var $my_var { value = "initial" }`
|
||||
- Update variable: `var.update $my_var { value = "new" }`
|
||||
- Loop through data: `foreach ($items) { each as $item { } }`
|
||||
- Conditional logic: `conditional { if ($condition) { } else { } }`
|
||||
- Transform data: `$data|filter_name:option`
|
||||
- Reference [docs/xanoscript-reference.md](docs/xanoscript-reference.md) for detailed syntax
|
||||
|
||||
5. **Test the implementation**:
|
||||
- Use Xano's built-in API testing interface
|
||||
- Verify database operations
|
||||
- Check response formats
|
||||
- Test error handling
|
||||
- Validate authentication flows
|
||||
|
||||
6. **Generate API documentation**:
|
||||
- Use MCP tools to generate OpenAPI specifications
|
||||
- Share with frontend developers
|
||||
- Document authentication requirements
|
||||
|
||||
7. **Deploy and monitor**:
|
||||
- Test in Xano's staging environment
|
||||
- Deploy to production
|
||||
- Monitor API usage and performance
|
||||
- Set up error logging
|
||||
|
||||
### Common Use Cases
|
||||
|
||||
**1. REST API for CRUD operations**:
|
||||
- Create database table
|
||||
- Generate default API endpoints
|
||||
- Add authentication
|
||||
- Customize response format
|
||||
|
||||
**2. Webhook receiver**:
|
||||
- Create API endpoint to receive webhooks
|
||||
- Parse incoming payload
|
||||
- Process data (store, transform, forward)
|
||||
- Return appropriate response
|
||||
|
||||
**3. External API integration**:
|
||||
- Create function to call external API
|
||||
- Handle authentication (API keys, OAuth)
|
||||
- Parse response and transform data
|
||||
- Store or return processed data
|
||||
|
||||
**4. Data transformation pipeline**:
|
||||
- Query source data
|
||||
- Apply filters and transformations
|
||||
- Aggregate or format results
|
||||
- Return processed data
|
||||
|
||||
**5. Scheduled background tasks**:
|
||||
- Create background task
|
||||
- Define schedule (cron-like)
|
||||
- Implement logic (cleanup, notifications, sync)
|
||||
- Handle errors and retries
|
||||
|
||||
### XanoScript Guidelines
|
||||
|
||||
**Be mindful of syntax differences**:
|
||||
- XanoScript is NOT JavaScript/Python
|
||||
- Function syntax: `namespace.function` (e.g., `db.query`, not `db_query`)
|
||||
- Variables always use `$` prefix: `$my_variable`
|
||||
- Assignment uses `as` keyword: `db.query users {} as $users`
|
||||
- Filters use pipe: `$users|count`, not `count($users)`
|
||||
|
||||
**When uncertain about syntax**:
|
||||
1. Check [docs/xanoscript-reference.md](docs/xanoscript-reference.md) for examples
|
||||
2. Ask Xano MCP to show existing function examples
|
||||
3. Test small snippets first
|
||||
4. Reference official documentation: https://docs.xano.com/xanoscript/key-concepts
|
||||
|
||||
**Common XanoScript mistakes**:
|
||||
- ❌ `db_query("users")` → ✅ `db.query users { }`
|
||||
- ❌ `var my_var = "value"` → ✅ `var $my_var { value = "value" }`
|
||||
- ❌ `count($array)` → ✅ `$array|count`
|
||||
- ❌ `if (condition) {}` → ✅ `conditional { if ($condition) { } }`
|
||||
|
||||
### Available MCP Tools
|
||||
|
||||
Once the plugin is enabled and environment variables are set, the Xano MCP server provides these tools automatically:
|
||||
|
||||
- **Database operations**: Creating and modifying tables, schemas, relationships
|
||||
- **API development**: Building endpoints, configuring routes and methods
|
||||
- **Authentication**: Managing API keys, JWT, OAuth configurations
|
||||
- **Custom functions**: Creating business logic with XanoScript
|
||||
- **Background tasks**: Setting up scheduled and triggered jobs
|
||||
- **Documentation**: Generating OpenAPI specs for frontend integration
|
||||
- **Workspace management**: Configuring settings and permissions
|
||||
|
||||
**Using MCP tools**: Simply ask Claude to perform Xano operations naturally (e.g., "Create a users table with email and name fields"). Claude will automatically use the appropriate Xano MCP tools to execute your requests.
|
||||
|
||||
### Error Handling
|
||||
|
||||
**Common issues**:
|
||||
- **Authentication errors**: Verify MCP token is correct and has required scopes
|
||||
- **XanoScript syntax errors**: Check function names, variable syntax, and filter usage
|
||||
- **Permission errors**: Ensure token has appropriate permissions for operation
|
||||
- **Rate limiting**: Xano has API rate limits - implement appropriate throttling
|
||||
|
||||
**Debugging approach**:
|
||||
1. Test XanoScript in Xano's visual editor first
|
||||
2. Validate data types and structure
|
||||
3. Check API endpoint configuration
|
||||
4. Review error messages carefully
|
||||
5. Use Xano's built-in debugger and logs
|
||||
|
||||
### Security Best Practices
|
||||
|
||||
1. **Token management**:
|
||||
- Never commit MCP tokens to version control
|
||||
- Use environment variables or secure storage
|
||||
- Rotate tokens periodically
|
||||
|
||||
2. **API security**:
|
||||
- Implement authentication on sensitive endpoints
|
||||
- Use API key authentication or JWT
|
||||
- Validate and sanitize all inputs
|
||||
- Implement rate limiting
|
||||
|
||||
3. **Data protection**:
|
||||
- Use appropriate field types (encrypted for sensitive data)
|
||||
- Implement proper access controls
|
||||
- Regular backups before significant changes
|
||||
- Test in non-production workspace first
|
||||
|
||||
## Examples
|
||||
|
||||
### Example 1: Create a Simple User Table and CRUD API
|
||||
|
||||
**User request**:
|
||||
```
|
||||
I need a backend to store user profiles with name, email, and bio
|
||||
```
|
||||
|
||||
**You would**:
|
||||
1. Use Xano MCP to create a table:
|
||||
- Table name: `users`
|
||||
- Fields:
|
||||
- `name` (text, required)
|
||||
- `email` (text, required, unique)
|
||||
- `bio` (text, optional)
|
||||
- `created_at` (timestamp, auto)
|
||||
|
||||
2. Generate CRUD API endpoints:
|
||||
- GET `/users` - List all users
|
||||
- GET `/users/{id}` - Get single user
|
||||
- POST `/users` - Create user
|
||||
- PUT `/users/{id}` - Update user
|
||||
- DELETE `/users/{id}` - Delete user
|
||||
|
||||
3. Add email validation to POST endpoint using XanoScript:
|
||||
```xanoscript
|
||||
conditional {
|
||||
if ($email|is_email|not) {
|
||||
response.error "Invalid email format"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
4. Test endpoints and return API documentation URLs
|
||||
|
||||
### Example 2: Build LinkedIn OAuth Callback Handler
|
||||
|
||||
**User request**:
|
||||
```
|
||||
Create an endpoint to receive LinkedIn OAuth callback and store the access token
|
||||
```
|
||||
|
||||
**You would**:
|
||||
1. Create database table:
|
||||
- Table name: `oauth_tokens`
|
||||
- Fields:
|
||||
- `user_id` (text)
|
||||
- `provider` (text) - "linkedin"
|
||||
- `access_token` (text, encrypted)
|
||||
- `refresh_token` (text, encrypted)
|
||||
- `expires_at` (timestamp)
|
||||
|
||||
2. Create API endpoint:
|
||||
- Method: `POST`
|
||||
- Path: `/oauth/linkedin/callback`
|
||||
- Inputs:
|
||||
- `code` (text, required) - OAuth authorization code
|
||||
- `user_id` (text, required)
|
||||
|
||||
3. Implement function stack logic:
|
||||
```xanoscript
|
||||
api oauth_callback {
|
||||
input {
|
||||
text code filters=required
|
||||
text user_id filters=required
|
||||
}
|
||||
|
||||
stack {
|
||||
// Exchange code for token (would call LinkedIn API)
|
||||
http.request {
|
||||
url = "https://www.linkedin.com/oauth/v2/accessToken"
|
||||
method = "POST"
|
||||
body = {
|
||||
grant_type: "authorization_code",
|
||||
code: $code,
|
||||
client_id: env.LINKEDIN_CLIENT_ID,
|
||||
client_secret: env.LINKEDIN_CLIENT_SECRET
|
||||
}
|
||||
} as $token_response
|
||||
|
||||
// Store token in database
|
||||
db.insert oauth_tokens {
|
||||
user_id = $user_id,
|
||||
provider = "linkedin",
|
||||
access_token = $token_response.access_token,
|
||||
refresh_token = $token_response.refresh_token,
|
||||
expires_at = $token_response.expires_in|timestamp_offset
|
||||
} as $stored_token
|
||||
|
||||
response = {
|
||||
success: true,
|
||||
token_id: $stored_token.id
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
4. Test the endpoint with sample OAuth code
|
||||
5. Return endpoint URL and usage instructions
|
||||
|
||||
### Example 3: Create Data Transformation Function
|
||||
|
||||
**User request**:
|
||||
```
|
||||
I need to format user data from the database before sending it to the frontend
|
||||
```
|
||||
|
||||
**You would**:
|
||||
1. Create custom function:
|
||||
- Name: `format_user_data`
|
||||
- Purpose: Transform database user records to API response format
|
||||
|
||||
2. Implement XanoScript logic:
|
||||
```xanoscript
|
||||
function format_user_data {
|
||||
input {
|
||||
object user_data
|
||||
}
|
||||
|
||||
stack {
|
||||
// Initialize result variable
|
||||
var $formatted {
|
||||
value = {}
|
||||
}
|
||||
|
||||
// Format timestamp
|
||||
var.update $formatted {
|
||||
value = $user_data|set:"created_at":($user_data.created_at|format_timestamp:"Y-m-d")
|
||||
}
|
||||
|
||||
// Add full name
|
||||
var.update $formatted {
|
||||
value = $formatted|set:"full_name":($user_data.first_name + " " + $user_data.last_name)
|
||||
}
|
||||
|
||||
// Remove sensitive fields
|
||||
var.update $formatted {
|
||||
value = $formatted|unset:"password_hash"|unset:"internal_notes"
|
||||
}
|
||||
|
||||
response = $formatted
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
3. Show how to use the function in API endpoint
|
||||
4. Test with sample data
|
||||
|
||||
### Example 4: Webhook Handler for External Service
|
||||
|
||||
**User request**:
|
||||
```
|
||||
Create a webhook endpoint to receive notifications from Stripe
|
||||
```
|
||||
|
||||
**You would**:
|
||||
1. Create API endpoint:
|
||||
- Method: `POST`
|
||||
- Path: `/webhooks/stripe`
|
||||
- Authentication: Verify Stripe signature
|
||||
|
||||
2. Implement handler logic:
|
||||
```xanoscript
|
||||
api stripe_webhook {
|
||||
input {
|
||||
text event_type
|
||||
object data
|
||||
}
|
||||
|
||||
stack {
|
||||
// Log the webhook event
|
||||
db.insert webhook_logs {
|
||||
source = "stripe",
|
||||
event_type = $event_type,
|
||||
payload = $data,
|
||||
received_at = timestamp.now
|
||||
}
|
||||
|
||||
// Handle different event types
|
||||
conditional {
|
||||
if ($event_type == "payment_intent.succeeded") {
|
||||
// Update order status
|
||||
db.update orders {
|
||||
where = { stripe_payment_id: $data.id },
|
||||
set = { status: "paid", paid_at: timestamp.now }
|
||||
}
|
||||
}
|
||||
elseif ($event_type == "customer.subscription.deleted") {
|
||||
// Handle subscription cancellation
|
||||
db.update users {
|
||||
where = { stripe_customer_id: $data.customer },
|
||||
set = { subscription_status: "cancelled" }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
response = { received: true }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
3. Configure Stripe webhook URL
|
||||
4. Test with Stripe webhook testing tool
|
||||
|
||||
### Example 5: Scheduled Background Task
|
||||
|
||||
**User request**:
|
||||
```
|
||||
Set up a daily task to clean up expired tokens
|
||||
```
|
||||
|
||||
**You would**:
|
||||
1. Create background task:
|
||||
- Name: `cleanup_expired_tokens`
|
||||
- Schedule: Daily at 2:00 AM
|
||||
- Type: Recurring
|
||||
|
||||
2. Implement cleanup logic:
|
||||
```xanoscript
|
||||
task cleanup_expired_tokens {
|
||||
stack {
|
||||
// Get current timestamp
|
||||
var $now { value = timestamp.now }
|
||||
|
||||
// Find expired tokens
|
||||
db.query oauth_tokens {
|
||||
where = {
|
||||
expires_at: { $lt: $now }
|
||||
}
|
||||
} as $expired_tokens
|
||||
|
||||
// Delete expired tokens
|
||||
foreach ($expired_tokens) {
|
||||
each as $token {
|
||||
db.delete oauth_tokens {
|
||||
where = { id: $token.id }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Log cleanup result
|
||||
db.insert task_logs {
|
||||
task_name = "cleanup_expired_tokens",
|
||||
tokens_deleted = $expired_tokens|count,
|
||||
executed_at = $now
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
3. Set up task schedule
|
||||
4. Test task execution manually
|
||||
5. Monitor task logs
|
||||
|
||||
## Summary
|
||||
|
||||
This skill enables building complete backend systems using Xano's no-code platform through MCP integration. Create databases, APIs, and business logic using XanoScript - Xano's custom syntax. Always reference the XanoScript documentation when uncertain about syntax, and test incrementally to ensure correct implementation.
|
||||
|
||||
**Key Resources**:
|
||||
- XanoScript Reference: [docs/xanoscript-reference.md](docs/xanoscript-reference.md)
|
||||
- Official Docs: https://docs.xano.com
|
||||
- MCP Setup: https://docs.xano.com/building/build-with-ai/xano-mcp
|
||||
850
docs/xanoscript-reference.md
Normal file
850
docs/xanoscript-reference.md
Normal file
@@ -0,0 +1,850 @@
|
||||
# XanoScript Reference Guide
|
||||
|
||||
Complete syntax reference for XanoScript, Xano's custom scripting language for building backend logic.
|
||||
|
||||
## Table of Contents
|
||||
|
||||
1. [Core Syntax](#core-syntax)
|
||||
2. [Primitives](#primitives)
|
||||
3. [Variables](#variables)
|
||||
4. [Functions](#functions)
|
||||
5. [Filters](#filters)
|
||||
6. [Loops](#loops)
|
||||
7. [Conditionals](#conditionals)
|
||||
8. [Common Patterns](#common-patterns)
|
||||
|
||||
---
|
||||
|
||||
## Core Syntax
|
||||
|
||||
### Basic Structure
|
||||
|
||||
All XanoScript code follows this pattern:
|
||||
|
||||
```xanoscript
|
||||
<primitive_keyword> <name> <attributes> {
|
||||
input { }
|
||||
stack { }
|
||||
response = <data>
|
||||
}
|
||||
```
|
||||
|
||||
**Example**:
|
||||
```xanoscript
|
||||
api get_users {
|
||||
input {
|
||||
int limit filters=min:1|max:100
|
||||
}
|
||||
|
||||
stack {
|
||||
db.query users { limit = $limit } as $users
|
||||
}
|
||||
|
||||
response = $users
|
||||
}
|
||||
```
|
||||
|
||||
### Namespace Notation
|
||||
|
||||
Functions are organized by namespace (category):
|
||||
- `db.*` - Database operations
|
||||
- `var.*` - Variable operations
|
||||
- `array.*` - Array operations
|
||||
- `http.*` - HTTP requests
|
||||
- `timestamp.*` - Date/time operations
|
||||
|
||||
**Format**: `namespace.function parameters { } as $variable`
|
||||
|
||||
---
|
||||
|
||||
## Primitives
|
||||
|
||||
Primitives are top-level constructs in Xano:
|
||||
|
||||
### API Endpoint
|
||||
|
||||
```xanoscript
|
||||
api endpoint_name {
|
||||
input { }
|
||||
stack { }
|
||||
response = $result
|
||||
}
|
||||
```
|
||||
|
||||
### Function
|
||||
|
||||
```xanoscript
|
||||
function function_name {
|
||||
input { }
|
||||
stack { }
|
||||
response = $result
|
||||
}
|
||||
```
|
||||
|
||||
### Background Task
|
||||
|
||||
```xanoscript
|
||||
task task_name {
|
||||
stack { }
|
||||
}
|
||||
```
|
||||
|
||||
### AI Agent
|
||||
|
||||
```xanoscript
|
||||
agent agent_name {
|
||||
input { }
|
||||
stack { }
|
||||
response = $result
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Variables
|
||||
|
||||
### Creating Variables
|
||||
|
||||
Use `var` keyword to initialize:
|
||||
|
||||
```xanoscript
|
||||
var $my_variable {
|
||||
value = "initial value"
|
||||
}
|
||||
```
|
||||
|
||||
**Examples**:
|
||||
```xanoscript
|
||||
var $counter { value = 0 }
|
||||
var $items { value = [] }
|
||||
var $user_data { value = {} }
|
||||
var $message { value = "Hello" }
|
||||
```
|
||||
|
||||
### Updating Variables
|
||||
|
||||
Use `var.update` to modify existing variables:
|
||||
|
||||
```xanoscript
|
||||
var.update $counter {
|
||||
value = $counter + 1
|
||||
}
|
||||
```
|
||||
|
||||
```xanoscript
|
||||
var.update $items {
|
||||
value = $items|push:$new_item
|
||||
}
|
||||
```
|
||||
|
||||
### Variable Assignment
|
||||
|
||||
Functions assign output using `as` keyword:
|
||||
|
||||
```xanoscript
|
||||
db.query users { } as $users
|
||||
http.request { url = "https://api.example.com" } as $response
|
||||
```
|
||||
|
||||
### Variable Naming
|
||||
|
||||
- Always prefix with `$`: `$variable_name`
|
||||
- Use snake_case: `$user_data`, `$formatted_items`
|
||||
- Descriptive names: `$active_users` not `$au`
|
||||
|
||||
---
|
||||
|
||||
## Functions
|
||||
|
||||
### Database Functions
|
||||
|
||||
**Query**:
|
||||
```xanoscript
|
||||
db.query table_name {
|
||||
where = { field: value },
|
||||
limit = 10,
|
||||
offset = 0,
|
||||
order = { field: "desc" }
|
||||
} as $results
|
||||
```
|
||||
|
||||
**Insert**:
|
||||
```xanoscript
|
||||
db.insert table_name {
|
||||
field1 = $value1,
|
||||
field2 = $value2
|
||||
} as $new_record
|
||||
```
|
||||
|
||||
**Update**:
|
||||
```xanoscript
|
||||
db.update table_name {
|
||||
where = { id: $user_id },
|
||||
set = { status: "active" }
|
||||
} as $updated_record
|
||||
```
|
||||
|
||||
**Delete**:
|
||||
```xanoscript
|
||||
db.delete table_name {
|
||||
where = { id: $record_id }
|
||||
}
|
||||
```
|
||||
|
||||
### Array Functions
|
||||
|
||||
**Push** (add to end):
|
||||
```xanoscript
|
||||
array.push $my_array {
|
||||
value = $new_item
|
||||
}
|
||||
```
|
||||
|
||||
**Pop** (remove from end):
|
||||
```xanoscript
|
||||
array.pop $my_array
|
||||
```
|
||||
|
||||
**Map** (transform each item):
|
||||
```xanoscript
|
||||
array.map $items {
|
||||
each as $item {
|
||||
return = $item.name
|
||||
}
|
||||
} as $names
|
||||
```
|
||||
|
||||
### HTTP Functions
|
||||
|
||||
**Request**:
|
||||
```xanoscript
|
||||
http.request {
|
||||
url = "https://api.example.com/data",
|
||||
method = "POST",
|
||||
headers = { "Authorization": "Bearer " + $token },
|
||||
body = { key: "value" }
|
||||
} as $response
|
||||
```
|
||||
|
||||
### Timestamp Functions
|
||||
|
||||
**Now**:
|
||||
```xanoscript
|
||||
timestamp.now as $current_time
|
||||
```
|
||||
|
||||
**Format**:
|
||||
```xanoscript
|
||||
timestamp.format $timestamp {
|
||||
format = "Y-m-d H:i:s"
|
||||
} as $formatted
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Filters
|
||||
|
||||
Filters transform data using pipe `|` syntax.
|
||||
|
||||
### Basic Syntax
|
||||
|
||||
```xanoscript
|
||||
$data|filter_name
|
||||
$data|filter_name:option
|
||||
$data|filter_name:option1:option2
|
||||
```
|
||||
|
||||
### Common Filters
|
||||
|
||||
**String Filters**:
|
||||
```xanoscript
|
||||
$text|upper // Convert to uppercase
|
||||
$text|lower // Convert to lowercase
|
||||
$text|capitalize // Capitalize first letter
|
||||
$text|trim // Remove whitespace
|
||||
$text|length // Get string length
|
||||
$email|is_email // Check if valid email
|
||||
```
|
||||
|
||||
**Array Filters**:
|
||||
```xanoscript
|
||||
$array|count // Count items
|
||||
$array|first // Get first item
|
||||
$array|last // Get last item
|
||||
$array|push:$item // Add item to end
|
||||
$array|is_empty // Check if empty
|
||||
```
|
||||
|
||||
**Object Filters**:
|
||||
```xanoscript
|
||||
$object|set:"key":$value // Set key to value
|
||||
$object|unset:"key" // Remove key
|
||||
$object|get:"key" // Get value by key
|
||||
$object|keys // Get all keys
|
||||
$object|values // Get all values
|
||||
```
|
||||
|
||||
**Numeric Filters**:
|
||||
```xanoscript
|
||||
$number|abs // Absolute value
|
||||
$number|round // Round to integer
|
||||
$number|ceil // Round up
|
||||
$number|floor // Round down
|
||||
```
|
||||
|
||||
**Timestamp Filters**:
|
||||
```xanoscript
|
||||
$timestamp|format_timestamp:"Y-m-d H:i:s"
|
||||
$timestamp|timestamp_offset:3600 // Add seconds
|
||||
```
|
||||
|
||||
**Logical Filters**:
|
||||
```xanoscript
|
||||
$value|not // Logical NOT
|
||||
$value|is_null // Check if null
|
||||
$value|is_empty // Check if empty
|
||||
```
|
||||
|
||||
### Chaining Filters
|
||||
|
||||
Filters can be chained:
|
||||
|
||||
```xanoscript
|
||||
$user.email|trim|lower|is_email
|
||||
$items|count|round
|
||||
$text|trim|upper|length
|
||||
```
|
||||
|
||||
### Filter Examples
|
||||
|
||||
**Validate email**:
|
||||
```xanoscript
|
||||
conditional {
|
||||
if ($email|trim|is_email|not) {
|
||||
response.error "Invalid email"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Transform array of objects**:
|
||||
```xanoscript
|
||||
foreach ($users) {
|
||||
each as $user {
|
||||
var.update $formatted_users {
|
||||
value = $formatted_users|push:($user|set:"name":($user.name|upper))
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Loops
|
||||
|
||||
### ForEach Loop
|
||||
|
||||
Iterate over arrays or collections:
|
||||
|
||||
```xanoscript
|
||||
foreach ($items) {
|
||||
each as $item {
|
||||
// Process each item
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**With Index**:
|
||||
```xanoscript
|
||||
foreach ($items) {
|
||||
each as $item, $index {
|
||||
// $index starts at 0
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Example**:
|
||||
```xanoscript
|
||||
var $formatted { value = [] }
|
||||
|
||||
foreach ($users) {
|
||||
each as $user {
|
||||
var $full_name {
|
||||
value = $user.first_name + " " + $user.last_name
|
||||
}
|
||||
|
||||
var.update $formatted {
|
||||
value = $formatted|push:{
|
||||
id: $user.id,
|
||||
name: $full_name
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### For Loop
|
||||
|
||||
Iterate a specific number of times:
|
||||
|
||||
```xanoscript
|
||||
for (10) {
|
||||
each as $index {
|
||||
// $index: 0, 1, 2, ... 9
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Example**:
|
||||
```xanoscript
|
||||
var $numbers { value = [] }
|
||||
|
||||
for (5) {
|
||||
each as $i {
|
||||
var.update $numbers {
|
||||
value = $numbers|push:($i + 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
// Result: [1, 2, 3, 4, 5]
|
||||
```
|
||||
|
||||
### While Loop
|
||||
|
||||
Repeat while condition is true:
|
||||
|
||||
```xanoscript
|
||||
while ($condition) {
|
||||
each {
|
||||
// Update condition to eventually exit
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Example**:
|
||||
```xanoscript
|
||||
var $count { value = 0 }
|
||||
|
||||
while ($count < 5) {
|
||||
each {
|
||||
var.update $count {
|
||||
value = $count + 1
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Conditionals
|
||||
|
||||
### If Statement
|
||||
|
||||
```xanoscript
|
||||
conditional {
|
||||
if ($condition) {
|
||||
// Execute if true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### If-Else
|
||||
|
||||
```xanoscript
|
||||
conditional {
|
||||
if ($condition) {
|
||||
// Execute if true
|
||||
}
|
||||
else {
|
||||
// Execute if false
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### If-ElseIf-Else
|
||||
|
||||
```xanoscript
|
||||
conditional {
|
||||
if ($condition1) {
|
||||
// First condition
|
||||
}
|
||||
elseif ($condition2) {
|
||||
// Second condition
|
||||
}
|
||||
elseif ($condition3) {
|
||||
// Third condition
|
||||
}
|
||||
else {
|
||||
// None matched
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Comparison Operators
|
||||
|
||||
In conditionals and filters:
|
||||
|
||||
```xanoscript
|
||||
$a == $b // Equals
|
||||
$a != $b // Not equals
|
||||
$a > $b // Greater than
|
||||
$a < $b // Less than
|
||||
$a >= $b // Greater than or equal
|
||||
$a <= $b // Less than or equal
|
||||
```
|
||||
|
||||
### Logical Operators
|
||||
|
||||
```xanoscript
|
||||
$a and $b // Logical AND
|
||||
$a or $b // Logical OR
|
||||
$value|not // Logical NOT (using filter)
|
||||
```
|
||||
|
||||
### Examples
|
||||
|
||||
**Validate input**:
|
||||
```xanoscript
|
||||
conditional {
|
||||
if ($email|is_empty) {
|
||||
response.error "Email is required"
|
||||
}
|
||||
elseif ($email|is_email|not) {
|
||||
response.error "Invalid email format"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Role-based logic**:
|
||||
```xanoscript
|
||||
conditional {
|
||||
if ($user.role == "admin") {
|
||||
db.query all_records { } as $records
|
||||
}
|
||||
elseif ($user.role == "user") {
|
||||
db.query records {
|
||||
where = { user_id: $user.id }
|
||||
} as $records
|
||||
}
|
||||
else {
|
||||
response.error "Unauthorized"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Common Patterns
|
||||
|
||||
### Pattern 1: API CRUD Endpoint
|
||||
|
||||
```xanoscript
|
||||
api create_user {
|
||||
input {
|
||||
text email filters=required|is_email
|
||||
text name filters=required|trim
|
||||
}
|
||||
|
||||
stack {
|
||||
// Validate unique email
|
||||
db.query users {
|
||||
where = { email: $email }
|
||||
} as $existing
|
||||
|
||||
conditional {
|
||||
if ($existing|count > 0) {
|
||||
response.error "Email already exists"
|
||||
}
|
||||
}
|
||||
|
||||
// Create user
|
||||
db.insert users {
|
||||
email = $email|lower|trim,
|
||||
name = $name|trim,
|
||||
created_at = timestamp.now
|
||||
} as $new_user
|
||||
|
||||
response = {
|
||||
success: true,
|
||||
user: $new_user
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Pattern 2: Data Transformation
|
||||
|
||||
```xanoscript
|
||||
function format_users {
|
||||
input {
|
||||
array raw_users
|
||||
}
|
||||
|
||||
stack {
|
||||
var $formatted { value = [] }
|
||||
|
||||
foreach ($raw_users) {
|
||||
each as $user {
|
||||
var $user_obj {
|
||||
value = {
|
||||
id: $user.id,
|
||||
name: $user.first_name + " " + $user.last_name,
|
||||
email: $user.email|lower,
|
||||
created: $user.created_at|format_timestamp:"Y-m-d"
|
||||
}
|
||||
}
|
||||
|
||||
var.update $formatted {
|
||||
value = $formatted|push:$user_obj
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
response = $formatted
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Pattern 3: External API Call
|
||||
|
||||
```xanoscript
|
||||
function fetch_external_data {
|
||||
input {
|
||||
text endpoint
|
||||
}
|
||||
|
||||
stack {
|
||||
http.request {
|
||||
url = "https://api.example.com/" + $endpoint,
|
||||
method = "GET",
|
||||
headers = {
|
||||
"Authorization": "Bearer " + env.API_KEY
|
||||
}
|
||||
} as $response
|
||||
|
||||
conditional {
|
||||
if ($response.status != 200) {
|
||||
response.error "API request failed"
|
||||
}
|
||||
}
|
||||
|
||||
response = $response.body
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Pattern 4: Conditional Processing
|
||||
|
||||
```xanoscript
|
||||
api process_order {
|
||||
input {
|
||||
int order_id
|
||||
text action
|
||||
}
|
||||
|
||||
stack {
|
||||
db.query orders {
|
||||
where = { id: $order_id }
|
||||
} as $order
|
||||
|
||||
conditional {
|
||||
if ($action == "approve") {
|
||||
db.update orders {
|
||||
where = { id: $order_id },
|
||||
set = {
|
||||
status: "approved",
|
||||
approved_at: timestamp.now
|
||||
}
|
||||
}
|
||||
}
|
||||
elseif ($action == "reject") {
|
||||
db.update orders {
|
||||
where = { id: $order_id },
|
||||
set = {
|
||||
status: "rejected",
|
||||
rejected_at: timestamp.now
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
response.error "Invalid action"
|
||||
}
|
||||
}
|
||||
|
||||
response = { success: true }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Pattern 5: Aggregate and Filter
|
||||
|
||||
```xanoscript
|
||||
api get_user_stats {
|
||||
input {
|
||||
int user_id
|
||||
}
|
||||
|
||||
stack {
|
||||
// Get user's orders
|
||||
db.query orders {
|
||||
where = { user_id: $user_id }
|
||||
} as $orders
|
||||
|
||||
// Calculate totals
|
||||
var $total_amount { value = 0 }
|
||||
var $completed_count { value = 0 }
|
||||
|
||||
foreach ($orders) {
|
||||
each as $order {
|
||||
var.update $total_amount {
|
||||
value = $total_amount + $order.amount
|
||||
}
|
||||
|
||||
conditional {
|
||||
if ($order.status == "completed") {
|
||||
var.update $completed_count {
|
||||
value = $completed_count + 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
response = {
|
||||
total_orders: $orders|count,
|
||||
completed_orders: $completed_count,
|
||||
total_spent: $total_amount
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Best Practices
|
||||
|
||||
### 1. Initialize Variables Early
|
||||
|
||||
```xanoscript
|
||||
// ✅ Good
|
||||
var $results { value = [] }
|
||||
foreach ($items) {
|
||||
each as $item {
|
||||
var.update $results { value = $results|push:$item }
|
||||
}
|
||||
}
|
||||
|
||||
// ❌ Bad - undefined variable
|
||||
foreach ($items) {
|
||||
each as $item {
|
||||
var.update $results { value = $results|push:$item } // Error!
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2. Use Filters for Validation
|
||||
|
||||
```xanoscript
|
||||
// ✅ Good
|
||||
conditional {
|
||||
if ($email|is_email|not) {
|
||||
response.error "Invalid email"
|
||||
}
|
||||
}
|
||||
|
||||
// ❌ Bad - manual validation
|
||||
conditional {
|
||||
if ($email.indexOf("@") == -1) { // Not XanoScript syntax
|
||||
response.error "Invalid email"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3. Chain Filters for Readability
|
||||
|
||||
```xanoscript
|
||||
// ✅ Good
|
||||
$user.email|trim|lower
|
||||
|
||||
// ❌ Less readable
|
||||
var $temp1 { value = $user.email|trim }
|
||||
var $temp2 { value = $temp1|lower }
|
||||
```
|
||||
|
||||
### 4. Use Meaningful Variable Names
|
||||
|
||||
```xanoscript
|
||||
// ✅ Good
|
||||
$active_users
|
||||
$formatted_response
|
||||
$total_amount
|
||||
|
||||
// ❌ Bad
|
||||
$au
|
||||
$fr
|
||||
$ta
|
||||
```
|
||||
|
||||
### 5. Handle Errors Gracefully
|
||||
|
||||
```xanoscript
|
||||
// ✅ Good
|
||||
http.request { url = $api_url } as $response
|
||||
|
||||
conditional {
|
||||
if ($response.status != 200) {
|
||||
response.error "External API failed: " + $response.status
|
||||
}
|
||||
}
|
||||
|
||||
// ❌ Bad - no error handling
|
||||
http.request { url = $api_url } as $response
|
||||
response = $response.body // Might fail if request errored
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Quick Reference
|
||||
|
||||
### Most Common Operations
|
||||
|
||||
**Database**:
|
||||
- `db.query table { } as $results` - Fetch records
|
||||
- `db.insert table { field = value } as $record` - Create record
|
||||
- `db.update table { where, set } as $record` - Update record
|
||||
- `db.delete table { where }` - Delete record
|
||||
|
||||
**Variables**:
|
||||
- `var $name { value = x }` - Create variable
|
||||
- `var.update $name { value = y }` - Update variable
|
||||
- `function_call { } as $result` - Assign function result
|
||||
|
||||
**Arrays**:
|
||||
- `$array|count` - Get length
|
||||
- `$array|first` - First item
|
||||
- `$array|last` - Last item
|
||||
- `$array|push:$item` - Add item
|
||||
|
||||
**Strings**:
|
||||
- `$str|trim` - Remove whitespace
|
||||
- `$str|upper` - Uppercase
|
||||
- `$str|lower` - Lowercase
|
||||
- `$str|is_email` - Validate email
|
||||
|
||||
**Control Flow**:
|
||||
- `conditional { if ($cond) { } }` - If statement
|
||||
- `foreach ($arr) { each as $item { } }` - Loop array
|
||||
- `for (n) { each as $i { } }` - Loop n times
|
||||
- `while ($cond) { each { } }` - While loop
|
||||
|
||||
---
|
||||
|
||||
## Additional Resources
|
||||
|
||||
- **Official Docs**: https://docs.xano.com/xanoscript/key-concepts
|
||||
- **Xano Transform**: https://docs.xano.com/xano-transform/using-xano-transform
|
||||
- **Function Reference**: https://www.xano.com/learn/Functions-Data-Transformation-Business-Logic/
|
||||
- **VS Code Extension**: https://marketplace.visualstudio.com/items?itemName=xano.xanoscript
|
||||
|
||||
---
|
||||
|
||||
**Remember**: XanoScript is unique to Xano. When in doubt, check examples in the Xano visual editor or reference this guide. Test syntax incrementally to ensure correctness.
|
||||
57
plugin.lock.json
Normal file
57
plugin.lock.json
Normal file
@@ -0,0 +1,57 @@
|
||||
{
|
||||
"$schema": "internal://schemas/plugin.lock.v1.json",
|
||||
"pluginId": "gh:djacobsmeyer/claude-skills-engineering:plugins/xano-backend-builder",
|
||||
"normalized": {
|
||||
"repo": null,
|
||||
"ref": "refs/tags/v20251128.0",
|
||||
"commit": "7183dd699f21069e6b45861a2b5e4a3034634a7b",
|
||||
"treeHash": "38ebe447e17b398454857c09b485689b6f52ba92681f6490680282f92422477a",
|
||||
"generatedAt": "2025-11-28T10:16:27.898187Z",
|
||||
"toolVersion": "publish_plugins.py@0.2.0"
|
||||
},
|
||||
"origin": {
|
||||
"remote": "git@github.com:zhongweili/42plugin-data.git",
|
||||
"branch": "master",
|
||||
"commit": "aa1497ed0949fd50e99e70d6324a29c5b34f9390",
|
||||
"repoRoot": "/Users/zhongweili/projects/openmind/42plugin-data"
|
||||
},
|
||||
"manifest": {
|
||||
"name": "xano-backend-builder",
|
||||
"description": "Build and manage no-code backend services with Xano using MCP server integration. Create database tables, API endpoints, custom functions, and business logic using XanoScript. Use when building backend APIs, database schemas, serverless functions, webhooks, or integrating with Xano workspace.",
|
||||
"version": "1.0.0"
|
||||
},
|
||||
"content": {
|
||||
"files": [
|
||||
{
|
||||
"path": ".mcp.json",
|
||||
"sha256": "f5a68d8c19336c1b9c4871a612a08c7680b1e2c6c095f78c81c757b4cb28f704"
|
||||
},
|
||||
{
|
||||
"path": "README.md",
|
||||
"sha256": "f727bb815baafec9ea79d23b20ce78dd53b79e62fc21a56ed333a11319619971"
|
||||
},
|
||||
{
|
||||
"path": "SKILL.md",
|
||||
"sha256": "111ed7446724fb9a6df4deca957253b802951b56a27c141d53875ebcbab64891"
|
||||
},
|
||||
{
|
||||
"path": ".env.example",
|
||||
"sha256": "84f874ba0608e2d160d56905d0428e7ce8300d70a5e3d2270b19789936ebd44e"
|
||||
},
|
||||
{
|
||||
"path": "docs/xanoscript-reference.md",
|
||||
"sha256": "3dcfc22947053304120e6a2bd366695c1530060924197c9cb8215f7bd1c70528"
|
||||
},
|
||||
{
|
||||
"path": ".claude-plugin/plugin.json",
|
||||
"sha256": "37cba9b979e79e0eac622017d5276c7a8e7d7e33216fad39c3fd8d31add77d32"
|
||||
}
|
||||
],
|
||||
"dirSha256": "38ebe447e17b398454857c09b485689b6f52ba92681f6490680282f92422477a"
|
||||
},
|
||||
"security": {
|
||||
"scannedAt": null,
|
||||
"scannerVersion": null,
|
||||
"flags": []
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user