Recipes
Complete, production-ready examples of common backend patterns and integrations. Copy and adapt these recipes for your own projects.
Authentication System
Complete User Registration
A robust registration endpoint with validation, duplicate checking, and token generation:
import { XanoScript } from './xano-script-library/lib/index.js';
const userRegistration = XanoScript.create('auth/register', 'POST')
.description('Complete user registration with validation')
.input('email', 'email', { required: true })
.input('password', 'password', { required: true })
.input('first_name', 'text', { required: true })
.input('last_name', 'text', { required: true })
// Validate password strength
.precondition(
'strlen($input.password) >= 8',
'Password must be at least 8 characters'
)
// Check if email already exists
.dbGet('"users"', { email: '$input.email' }, 'existing')
.precondition('$existing == null', 'Email already registered')
// Hash password and create user
.hashPassword('$input.password', 'password_hash')
.dbAdd('"users"', {
email: '$input.email',
password: '$password_hash',
first_name: '$input.first_name',
last_name: '$input.last_name',
status: 'active',
created_at: 'now'
}, 'new_user')
// Generate auth token
.createToken({
user_id: '$new_user.id',
email: '$new_user.email',
role: 'user'
}, 86400, 'auth_token')
.response({
user: {
id: '$new_user.id',
email: '$new_user.email',
first_name: '$new_user.first_name',
last_name: '$new_user.last_name'
},
token: '$auth_token'
});
User Login with Rate Limiting
Secure login with brute force protection:
const userLogin = XanoScript.create('auth/login', 'POST')
.description('User login with rate limiting')
.input('email', 'email', { required: true })
.input('password', 'password', { required: true })
// Check for rate limiting
.dbGet('"login_attempts"', { email: '$input.email' }, 'attempts')
.conditional('$attempts != null && $attempts.count >= 5')
.then()
.return({
error: 'Too many login attempts. Try again in 15 minutes.'
})
.endConditional()
// Find user
.dbGet('"users"', { email: '$input.email' }, 'user')
.conditional('$user == null')
.then()
// Record failed attempt
.conditional('$attempts == null')
.then()
.dbAdd('"login_attempts"', {
email: '$input.email',
count: 1,
last_attempt: 'now'
}, 'new_attempt')
.else()
.dbEdit('"login_attempts"',
{ email: '$input.email' },
{
count: '$attempts.count + 1',
last_attempt: 'now'
},
'updated_attempt'
)
.endConditional()
.return({ error: 'Invalid credentials' })
.endConditional()
.precondition('$user.status == "active"', 'Account is inactive')
// Verify password
.verifyPassword('$input.password', '$user.password', 'is_valid')
.conditional('!$is_valid')
.then()
// Record failed attempt
.dbEdit('"login_attempts"',
{ email: '$input.email' },
{ count: '$attempts.count + 1' },
'updated_attempt'
)
.return({ error: 'Invalid credentials' })
.endConditional()
// Clear failed attempts on successful login
.conditional('$attempts != null')
.then()
.dbDelete('"login_attempts"', { email: '$input.email' }, 'cleared')
.endConditional()
// Update last login
.dbEdit('"users"',
{ id: '$user.id' },
{ last_login: 'now' },
'updated_user'
)
// Generate token
.createToken({
user_id: '$user.id',
email: '$user.email',
role: '$user.role'
}, 86400, 'auth_token')
.response({
user: {
id: '$user.id',
email: '$user.email',
first_name: '$user.first_name',
last_name: '$user.last_name',
role: '$user.role'
},
token: '$auth_token'
});
Paginated Search API
Product Search with Filters
A complete search endpoint with pagination, filtering, and sorting:
const productSearch = XanoScript.create('products/search', 'GET')
.description('Search products with filters and pagination')
.input('search', 'text')
.input('category', 'text')
.input('min_price', 'decimal')
.input('max_price', 'decimal')
.input('status', 'text', { default: 'active' })
.input('page', 'int', { default: 1 })
.input('per_page', 'int', { default: 20 })
.input('sort_by', 'text', { default: 'created_at' })
.input('sort_dir', 'text', { default: 'desc' })
// Build filters object
.var('filters', 'object', { status: '$input.status' })
.conditional('$input.category != null')
.then()
.var('filters', 'object', {
status: '$input.status',
category: '$input.category'
})
.endConditional()
.conditional('$input.min_price != null && $input.max_price != null')
.then()
.var('filters', 'object', {
status: '$input.status',
category: '$input.category',
price: '>= $input.min_price && <= $input.max_price'
})
.endConditional()
// Query products
.dbQuery('"products"', {
search: '$input.search',
filters: '$filters',
pagination: {
page: '$input.page',
per_page: '$input.per_page'
},
sort: {
field: '$input.sort_by',
direction: '$input.sort_dir'
}
})
.response({
products: '$results.items',
pagination: {
page: '$results.curPage',
per_page: '$results.perPage',
total: '$results.itemsTotal',
total_pages: 'ceil($results.itemsTotal / $results.perPage)',
has_next: '$results.nextPage != null',
has_prev: '$results.prevPage != null'
},
filters: {
search: '$input.search',
category: '$input.category',
price_range: {
min: '$input.min_price',
max: '$input.max_price'
}
}
});
Order Processing
Create Order with Validation
Process an order with inventory checking and total calculation:
const createOrder = XanoScript.create('orders/create', 'POST')
.description('Create order with inventory validation')
.requiresAuth('users')
.input('items', 'object', { required: true })
.input('shipping_address', 'object', { required: true })
// Validate items not empty
.precondition('count($input.items) > 0', 'Order must have at least one item')
.var('total', 'decimal', 0)
.var('validated_items', 'array', [])
// Validate each item and calculate total
.forEach('$input.items', '$item')
.precondition('$item.product_id != null', 'Product ID required')
.precondition('$item.quantity > 0', 'Quantity must be positive')
// Get product
.dbGet('"products"', { id: '$item.product_id' }, 'product')
.precondition('$product != null', 'Product not found')
.precondition('$product.status == "active"', 'Product not available')
// Check inventory
.precondition(
'$product.stock >= $item.quantity',
'Insufficient stock for product: $product.name'
)
// Calculate item total
.var('item_total', 'decimal', '$product.price * $item.quantity')
.var('total', 'decimal', '$total + $item_total')
// Add to validated items
.var('validated_item', 'object', {
product_id: '$product.id',
product_name: '$product.name',
quantity: '$item.quantity',
price: '$product.price',
total: '$item_total'
})
.arrayPush('$validated_items', '$validated_item')
.endForEach()
// Create order
.dbAdd('"orders"', {
user_id: '$auth.id',
total: '$total',
status: 'pending',
shipping_address: '$input.shipping_address',
created_at: 'now'
}, 'new_order')
// Create order items and update inventory
.forEach('$validated_items', '$item')
.dbAdd('"order_items"', {
order_id: '$new_order.id',
product_id: '$item.product_id',
quantity: '$item.quantity',
price: '$item.price',
total: '$item.total'
}, 'order_item')
// Reduce stock
.dbGet('"products"', { id: '$item.product_id' }, 'product')
.dbEdit('"products"',
{ id: '$item.product_id' },
{ stock: '$product.stock - $item.quantity' },
'updated_product'
)
.endForEach()
.response({
order: {
id: '$new_order.id',
total: '$new_order.total',
status: '$new_order.status',
created_at: '$new_order.created_at'
},
items: '$validated_items',
item_count: 'count($validated_items)'
});
Process Bulk Orders
Handle multiple orders in one request:
const bulkProcessOrders = XanoScript.create('orders/bulk-process', 'POST')
.description('Process multiple orders with status update')
.requiresAuth('users')
.input('order_ids', 'object', { required: true })
.input('action', 'text', { required: true })
// Verify admin role
.precondition('$auth.role == "admin"', 'Admin access required')
// Validate action
.precondition(
'$input.action == "approve" || $input.action == "cancel" || $input.action == "ship"',
'Invalid action'
)
.var('processed', 'int', 0)
.var('failed', 'int', 0)
.var('results', 'array', [])
.forEach('$input.order_ids', '$order_id')
// Get order
.dbGet('"orders"', { id: '$order_id' }, 'order')
.conditional('$order == null')
.then()
.var('failed', 'int', '$failed + 1')
.var('result', 'object', {
order_id: '$order_id',
success: false,
reason: 'Order not found'
})
.arrayPush('$results', '$result')
.elseIf('$order.status == "cancelled"')
.then()
.var('failed', 'int', '$failed + 1')
.var('result', 'object', {
order_id: '$order_id',
success: false,
reason: 'Order already cancelled'
})
.arrayPush('$results', '$result')
.else()
// Process order
.dbEdit('"orders"',
{ id: '$order_id' },
{
status: '$input.action',
processed_by: '$auth.id',
processed_at: 'now'
},
'updated_order'
)
.var('processed', 'int', '$processed + 1')
.var('result', 'object', {
order_id: '$order_id',
success: true,
new_status: '$input.action'
})
.arrayPush('$results', '$result')
.endConditional()
.endForEach()
.response({
total: 'count($input.order_ids)',
processed: '$processed',
failed: '$failed',
results: '$results'
});
External API Integration
Weather Service Integration
Fetch and cache weather data:
const getWeather = XanoScript.create('integrations/weather', 'GET')
.description('Get weather data with caching')
.input('city', 'text', { required: true })
.var('cache_key', 'text', 'weather_$input.city')
// Check cache
.redisGet('$cache_key', 'cached_weather')
.conditional('$cached_weather != null')
.then()
.response({
temperature: '$cached_weather.temp',
description: '$cached_weather.description',
city: '$input.city',
cached: true
})
.endConditional()
// Fetch from API
.var('api_key', 'text', 'env.WEATHER_API_KEY')
.apiRequest(
'https://api.openweathermap.org/data/2.5/weather',
'GET',
{
params: {
q: '$input.city',
appid: '$api_key',
units: 'metric'
}
},
'api_response'
)
.conditional('$api_response.response.status != 200')
.then()
.return({
error: 'Failed to fetch weather data',
status: '$api_response.response.status'
})
.endConditional()
// Cache result for 30 minutes
.var('weather_data', 'object', {
temp: '$api_response.response.data.main.temp',
description: '$api_response.response.data.weather[0].description'
})
.redisSet('$cache_key', '$weather_data', 1800)
.response({
temperature: '$weather_data.temp',
description: '$weather_data.description',
city: '$api_response.response.data.name',
cached: false
});
Stripe Payment Processing
Create and process a payment:
const createPaymentIntent = XanoScript.create('payments/create', 'POST')
.description('Create Stripe payment intent')
.requiresAuth('users')
.input('amount', 'int', { required: true })
.input('currency', 'text', { default: 'usd' })
.input('description', 'text')
// Validate amount
.precondition('$input.amount >= 50', 'Minimum amount is $0.50')
.var('stripe_key', 'text', 'env.STRIPE_SECRET_KEY')
// Create payment intent
.apiRequest(
'https://api.stripe.com/v1/payment_intents',
'POST',
{
headers: [
{ key: 'Authorization', value: 'Bearer $stripe_key' },
{ key: 'Content-Type', value: 'application/x-www-form-urlencoded' }
],
body: {
amount: '$input.amount',
currency: '$input.currency',
description: '$input.description',
metadata: {
user_id: '$auth.id',
user_email: '$auth.email'
}
}
},
'stripe_response'
)
.conditional('$stripe_response.response.status != 200')
.then()
.return({
error: 'Failed to create payment intent',
message: '$stripe_response.response.data.error.message'
})
.endConditional()
.var('payment_intent', 'object', '$stripe_response.response.data')
// Save to database
.dbAdd('"payments"', {
user_id: '$auth.id',
stripe_payment_intent_id: '$payment_intent.id',
amount: '$input.amount',
currency: '$input.currency',
status: '$payment_intent.status',
description: '$input.description',
created_at: 'now'
}, 'payment_record')
.response({
payment_id: '$payment_record.id',
client_secret: '$payment_intent.client_secret',
amount: '$input.amount',
currency: '$input.currency',
status: '$payment_intent.status'
});
Role-Based Access Control
Advanced Permission System
Implement granular permission checking:
const checkPermission = XanoScript.create('auth/check-permission', 'POST')
.description('Check user permissions for resource and action')
.requiresAuth('users')
.input('resource', 'text', { required: true })
.input('action', 'text', { required: true })
.input('resource_id', 'int')
.var('has_permission', 'bool', false)
// Admin has all permissions
.conditional('$auth.role == "admin"')
.then()
.var('has_permission', 'bool', true)
// Moderators can read and update
.elseIf('$auth.role == "moderator"')
.then()
.conditional('$input.action == "read" || $input.action == "update"')
.then()
.var('has_permission', 'bool', true)
.endConditional()
// Users have limited permissions
.elseIf('$auth.role == "user"')
.then()
.conditional('$input.action == "read"')
.then()
.var('has_permission', 'bool', true)
.elseIf('$input.action == "update" || $input.action == "delete"')
.then()
// Check resource ownership
.conditional('$input.resource_id != null')
.then()
.dbGet('"$input.resource"', { id: '$input.resource_id' }, 'resource')
.conditional('$resource != null && $resource.user_id == $auth.id')
.then()
.var('has_permission', 'bool', true)
.endConditional()
.endConditional()
.endConditional()
.endConditional()
.response({
granted: '$has_permission',
resource: '$input.resource',
action: '$input.action',
user: {
id: '$auth.id',
role: '$auth.role'
}
});
Next Steps
- Database Operations - Learn more database patterns
- API Integration - Explore API integration options
- Security & Auth - Implement security best practices
- API Reference - Complete method documentation