API Integration

Connect your Xano backend to external services using the SDK’s comprehensive API request capabilities. Make HTTP requests, handle responses, and integrate with third-party APIs.

Basic HTTP Requests

GET Requests

Fetch data from external APIs:

.apiRequest(
  'https://api.example.com/users',
  'GET',
  {
    params: {
      page: '$input.page',
      limit: 20
    },
    headers: [
      { key: 'Authorization', value: 'Bearer $api_key' }
    ]
  },
  'api_response'
)

The response is stored in the specified alias and contains:

  • response.status - HTTP status code
  • response.data - Response body
  • response.headers - Response headers

POST Requests

Send data to external APIs:

.apiRequest(
  'https://api.example.com/users',
  'POST',
  {
    headers: [
      { key: 'Content-Type', value: 'application/json' },
      { key: 'Authorization', value: 'Bearer $api_key' }
    ],
    body: {
      name: '$input.name',
      email: '$input.email',
      role: 'user'
    }
  },
  'create_response'
)

PUT and PATCH Requests

Update resources on external APIs:

.apiRequest(
  'https://api.example.com/users/$input.user_id',
  'PUT',
  {
    headers: [
      { key: 'Content-Type', value: 'application/json' },
      { key: 'Authorization', value: 'Bearer $api_key' }
    ],
    body: {
      name: '$input.name',
      email: '$input.email',
      updated_at: 'now'
    }
  },
  'update_response'
)

DELETE Requests

Remove resources from external APIs:

.apiRequest(
  'https://api.example.com/users/$input.user_id',
  'DELETE',
  {
    headers: [
      { key: 'Authorization', value: 'Bearer $api_key' }
    ]
  },
  'delete_response'
)

Request Configuration

Headers

Add custom headers to your requests:

.apiRequest(
  'https://api.example.com/data',
  'GET',
  {
    headers: [
      { key: 'Authorization', value: 'Bearer $env.API_KEY' },
      { key: 'X-Custom-Header', value: '$input.custom_value' },
      { key: 'Accept', value: 'application/json' }
    ]
  },
  'response'
)

Query Parameters

Add query parameters to GET requests:

.apiRequest(
  'https://api.example.com/search',
  'GET',
  {
    params: {
      q: '$input.search',
      category: '$input.category',
      limit: 50,
      offset: '$input.offset'
    }
  },
  'search_results'
)

The URL will be constructed as:

https://api.example.com/search?q=value&category=electronics&limit=50&offset=0

Request Body

Send JSON data in POST, PUT, and PATCH requests:

.apiRequest(
  'https://api.example.com/orders',
  'POST',
  {
    headers: [
      { key: 'Content-Type', value: 'application/json' }
    ],
    body: {
      user_id: '$auth.id',
      items: '$cart.items',
      total: '$calculated_total',
      shipping_address: '$input.address'
    }
  },
  'order_response'
)

Timeouts

Set request timeouts to prevent hanging:

.apiRequest(
  'https://slow-api.example.com/data',
  'GET',
  {
    timeout: 5000  // 5 seconds
  },
  'response'
)

Response Handling

Checking Status Codes

Verify the response was successful:

.apiRequest(
  'https://api.example.com/data',
  'GET',
  {},
  'api_response'
)
 
.conditional('$api_response.response.status == 200')
  .then()
  .var('data', 'object', '$api_response.response.data')
  .response({ data: '$data' })
.else()
  .return({ error: 'API request failed' })
.endConditional()

Extracting Response Data

Access specific fields from the API response:

.apiRequest(
  'https://api.weather.com/current',
  'GET',
  {
    params: { city: '$input.city' }
  },
  'weather_response'
)
 
.var('temperature', 'decimal', '$weather_response.response.data.temp')
.var('condition', 'text', '$weather_response.response.data.condition')
 
.response({
  temperature: '$temperature',
  condition: '$condition',
  city: '$input.city'
})

Error Handling

Handle API errors gracefully:

.apiRequest(
  'https://api.example.com/data',
  'GET',
  {},
  'api_response'
)
 
.conditional('$api_response.response.status >= 400')
  .then()
  .var('error_message', 'text', '$api_response.response.data.message')
  .return({
    error: '$error_message',
    status: '$api_response.response.status'
  })
.endConditional()
 
// Continue with successful response
.response({
  data: '$api_response.response.data'
})

GraphQL Integration

Basic GraphQL Queries

Query GraphQL APIs:

.graphqlRequest(
  'https://api.example.com/graphql',
  `
    query GetUser($id: ID!) {
      user(id: $id) {
        id
        name
        email
        posts {
          title
          content
        }
      }
    }
  `,
  {
    id: '$input.user_id'
  },
  [
    { key: 'Authorization', value: 'Bearer $api_key' }
  ],
  'graphql_response'
)
 
.response({
  user: '$graphql_response.response.data.user'
})

GraphQL Mutations

Modify data through GraphQL:

.graphqlRequest(
  'https://api.example.com/graphql',
  `
    mutation CreatePost($title: String!, $content: String!) {
      createPost(title: $title, content: $content) {
        id
        title
        createdAt
      }
    }
  `,
  {
    title: '$input.title',
    content: '$input.content'
  },
  [
    { key: 'Authorization', value: 'Bearer $auth.token' }
  ],
  'create_response'
)
 
.response({
  post: '$create_response.response.data.createPost'
})

Common Integration Patterns

Weather API Integration

Fetch weather data from external service:

const weatherEndpoint = XanoScript.create('integrations/weather', 'GET')
  .description('Get current weather for a city')
  .input('city', 'text', { required: true })
  
  .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'
      }
    },
    'weather_response'
  )
  
  .conditional('$weather_response.response.status == 200')
    .then()
    .response({
      temperature: '$weather_response.response.data.main.temp',
      description: '$weather_response.response.data.weather[0].description',
      humidity: '$weather_response.response.data.main.humidity',
      city: '$weather_response.response.data.name'
    })
  .else()
    .return({ error: 'Failed to fetch weather data' })
  .endConditional();

Stripe Payment Processing

Create a payment intent:

const createPayment = XanoScript.create('payments/create', 'POST')
  .description('Create Stripe payment intent')
  .requiresAuth('users')
  .input('amount', 'int', { required: true })
  .input('currency', 'text', { default: 'usd' })
  
  .var('stripe_key', 'text', 'env.STRIPE_SECRET_KEY')
  
  .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',
        metadata: {
          user_id: '$auth.id'
        }
      }
    },
    'payment_response'
  )
  
  .conditional('$payment_response.response.status == 200')
    .then()
    .var('payment_intent', 'object', '$payment_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',
      created_at: 'now'
    }, 'payment_record')
    
    .response({
      client_secret: '$payment_intent.client_secret',
      payment_id: '$payment_record.id'
    })
  .else()
    .return({ error: 'Failed to create payment intent' })
  .endConditional();

Slack Notifications

Send messages to Slack:

const sendSlackNotification = XanoScript.create('notifications/slack', 'POST')
  .description('Send notification to Slack channel')
  .requiresAuth('users')
  .input('message', 'text', { required: true })
  .input('channel', 'text', { default: '#general' })
  
  .var('webhook_url', 'text', 'env.SLACK_WEBHOOK_URL')
  
  .apiRequest(
    '$webhook_url',
    'POST',
    {
      headers: [
        { key: 'Content-Type', value: 'application/json' }
      ],
      body: {
        channel: '$input.channel',
        text: '$input.message',
        username: 'Xano Bot',
        icon_emoji: ':robot_face:'
      }
    },
    'slack_response'
  )
  
  .conditional('$slack_response.response.status == 200')
    .then()
    .response({ sent: true, message: '$input.message' })
  .else()
    .return({ error: 'Failed to send Slack notification' })
  .endConditional();

Email Service Integration

Send emails via SendGrid:

const sendEmail = XanoScript.create('email/send', 'POST')
  .description('Send email via SendGrid')
  .requiresAuth('users')
  .input('to', 'email', { required: true })
  .input('subject', 'text', { required: true })
  .input('content', 'text', { required: true })
  
  .var('sendgrid_key', 'text', 'env.SENDGRID_API_KEY')
  
  .apiRequest(
    'https://api.sendgrid.com/v3/mail/send',
    'POST',
    {
      headers: [
        { key: 'Authorization', value: 'Bearer $sendgrid_key' },
        { key: 'Content-Type', value: 'application/json' }
      ],
      body: {
        personalizations: [{
          to: [{ email: '$input.to' }]
        }],
        from: { email: 'noreply@example.com' },
        subject: '$input.subject',
        content: [{
          type: 'text/html',
          value: '$input.content'
        }]
      }
    },
    'email_response'
  )
  
  .conditional('$email_response.response.status == 202')
    .then()
    .dbAdd('"email_log"', {
      user_id: '$auth.id',
      to: '$input.to',
      subject: '$input.subject',
      sent_at: 'now',
      status: 'sent'
    }, 'log')
    .response({ sent: true })
  .else()
    .return({ error: 'Failed to send email' })
  .endConditional();

Authentication Patterns

API Key Authentication

Pass API keys in headers:

.var('api_key', 'text', 'env.EXTERNAL_API_KEY')
 
.apiRequest(
  'https://api.example.com/data',
  'GET',
  {
    headers: [
      { key: 'X-API-Key', value: '$api_key' }
    ]
  },
  'response'
)

Bearer Token Authentication

Use bearer tokens for OAuth:

.apiRequest(
  'https://api.example.com/user',
  'GET',
  {
    headers: [
      { key: 'Authorization', value: 'Bearer $auth.external_token' }
    ]
  },
  'user_data'
)

Basic Authentication

Encode credentials for basic auth:

.var('username', 'text', 'env.API_USERNAME')
.var('password', 'text', 'env.API_PASSWORD')
.var('credentials', 'text', 'base64_encode($username + ":" + $password)')
 
.apiRequest(
  'https://api.example.com/data',
  'GET',
  {
    headers: [
      { key: 'Authorization', value: 'Basic $credentials' }
    ]
  },
  'response'
)

Best Practices

Environment Variables

Store sensitive API keys and credentials in environment variables:

.var('api_key', 'text', 'env.EXTERNAL_API_KEY')
.var('api_secret', 'text', 'env.EXTERNAL_API_SECRET')

Never hardcode credentials in your endpoints.

Error Handling

Always check response status codes and handle errors:

.conditional('$api_response.response.status >= 400')
  .then()
  .return({
    error: true,
    message: '$api_response.response.data.message',
    status: '$api_response.response.status'
  })
.endConditional()

Rate Limiting

Respect API rate limits and implement retry logic:

.conditional('$api_response.response.status == 429')
  .then()
  .return({
    error: 'Rate limit exceeded',
    retry_after: '$api_response.response.headers.retry-after'
  })
.endConditional()

Request Validation

Validate inputs before making external requests:

.precondition('$input.email != null', 'Email is required')
.precondition('$input.amount > 0', 'Amount must be positive')
 
.apiRequest(...)

Logging

Log API requests for debugging and monitoring:

.apiRequest(
  'https://api.example.com/data',
  'POST',
  { body: '$input.data' },
  'api_response'
)
 
.dbAdd('"api_logs"', {
  endpoint: 'external_api',
  request_data: '$input.data',
  response_status: '$api_response.response.status',
  created_at: 'now'
}, 'log')

Next Steps