Services

Services handle external data operations - API calls and custom functions that power your form's dynamic behavior.

🔗

What Are Services?

Services are reusable operations that handle data fetching, API communication, and custom business logic. They're called by lifecycle hooks, actions, and validations to create dynamic form experiences.

Two Types of Services

🌐REST API Services

Call external REST APIs with full control over HTTP methods, headers, and request/response handling.

json
{
  "type": "rest",
  "url": "/api/users/{{context:userId}}",
  "method": "GET",
  "headers": {
    "Authorization": "Bearer {{context:authToken}}"
  }
}

Function Services

Execute custom JavaScript functions for complex logic, data transformation, and calculations.

json
{
  "type": "function",
  "function": "calculateTotal"
}

REST API Service Structure

Complete REST Service Definition

json
{
  "userApiService": {
    "type": "rest",
    "url": "/v1/users/{{context:userId}}",
    "method": "GET",
    "baseURL": "https://jsonplaceholder.typicode.com",
    "headers": {
      "Accept": "application/json"
    }
  },
  "orderApiService": {
    "type": "rest",
    "url": "/api/orders",
    "method": "POST",
    "format": "json",
    "body": {
      "userId": "{{context:user.id}}",
      "email": "{{context:form.email}}",
      "data": "{{event:customData}}"
    },
    "baseURL": "https://api.example.com",
    "headers": {
      "Content-Type": "application/json",
      "Authorization": "Bearer {{context:auth.token}}",
      "X-Custom-Header": "value"
    },
    "bearer": "{{context:auth.bearerToken}}",
    "auth": {
      "username": "{{context:credentials.username}}",
      "password": "{{context:credentials.password}}"
    }
  }
}

REST Service Properties:

1. type: "rest"

Always "rest" for HTTP API calls

2. url

API endpoint path (can include context placeholders)

/api/users/{{context:userId}}

3. method

HTTP method (GET, POST, PUT, DELETE, PATCH)

4. format

Request body format: "json" or "formData"

5. body

Request payload (for POST/PUT/PATCH)

6. baseURL

Base URL for the API (optional)

Example: "https://api.example.com"

Combines with url to create full endpoint:

baseURL: "https://api.example.com" + url: "/users" = "https://api.example.com/users"

7. headers

Custom HTTP headers (supports context placeholders)

8. bearer

Bearer token for Authorization header

9. auth

Basic authentication credentials

Placeholder Syntax

PlaceholderDescriptionExample
{{context:path}}Access form context data{{context:user.email}}
{{secret:path}}Access encrypted secret data (🔒 secure){{secret:apiToken}}
{{event:property}}Access event/lifecycle data{{event:userId}}
{{block:path}}Access current block data{{block:id}}

🔒 Security & Encrypted Secrets

⚠️

Never Store Sensitive Data in Plain Text

API tokens, passwords, and credentials should never be stored in regular context. Use {{secret:path}} placeholders with encrypted secret context instead.

Using Encrypted Secrets

For sensitive data like API tokens and passwords, use the secretContext with {{secret:path}} placeholders:

json
{
  "fetchUserData": {
    "type": "rest",
    "url": "/api/user",
    "method": "GET",
    "bearer": "{{secret:apiToken}}"
  },
  "authenticateUser": {
    "type": "rest",
    "url": "/api/auth",
    "method": "POST",
    "auth": {
      "username": "{{secret:apiUsername}}",
      "password": "{{secret:apiPassword}}"
    }
  },
  "callExternalAPI": {
    "type": "rest",
    "url": "/api/data",
    "headers": {
      "X-API-Key": "{{secret:externalApiKey}}"
    }
  }
}

Mixed Context & Secrets

You can safely combine regular context values with encrypted secrets:

json
{
  "getUserOrders": {
    "type": "rest",
    "url": "/api/users/{{context:userId}}/orders",
    "method": "GET",
    "bearer": "{{secret:apiToken}}",
    "headers": {
      "X-Request-ID": "{{context:requestId}}"
    }
  }
}

✅ Security Best Practices

  • • Use {{secret:}} for tokens, passwords, API keys
  • • Use {{context:}} for user IDs, form data, non-sensitive info
  • • Store encryption keys in environment variables
  • • Never hardcode sensitive data in JSON configs
  • • Enable HTTPS in production

→ Learn more about Security & Encrypted Secrets

Function Service Structure

Function Service Definition

json
{
  "calculateTax": {
    "type": "function",
    "function": "calculateTax"
  },
  "formatName": {
    "type": "function",
    "function": "formatUserName"
  }
}

Function Service Properties:

1. type: "function"

Always "function" for custom functions

2. function

Name of the JavaScript function to call

Function Implementation

Functions are implemented as JavaScript functions that receive context and return data:

javascript
// In your application code
function calculateTax(context) {
  const subtotal = context.order.subtotal;
  const taxRate = context.location.taxRate || 0.08;
  return Math.round(subtotal * taxRate * 100) / 100;
}

function formatUserName(context) {
  const first = context.user.firstName;
  const last = context.user.lastName;
  return `${first} ${last}`.trim();
}

Common Service Patterns

1. Data Fetching

json
{
  "loadUserProfile": {
    "type": "rest",
    "url": "/api/users/{{context:userId}}",
    "method": "GET"
  }
}

2. Form Submission

json
{
  "submitForm": {
    "type": "rest",
    "url": "/api/submit",
    "method": "POST",
    "format": "formData",
    "body": {
      "name": "{{context:form.name}}",
      "email": "{{context:form.email}}",
      "file": "{{context:form.uploadedFile}}"
    }
  }
}

3. Auto-lookup Services

json
{
  "lookupCity": {
    "type": "rest",
    "url": "/api/lookup/city/{{context:zipCode}}",
    "method": "GET"
  },
  "validateEmail": {
    "type": "function",
    "function": "validateEmailFormat"
  }
}

4. Complex Calculations

json
{
  "calculateTotal": {
    "type": "function",
    "function": "calculateOrderTotal"
  },
  "applyDiscount": {
    "type": "function",
    "function": "applyCouponDiscount"
  }
}

Error Handling

Services can fail, so always handle errors in your lifecycle hooks:

json
{
  "lifecycle": {
    "onFormLoad": [
      {
        "type": "callApi",
        "service": "loadUserProfile",
        "then": [
          {
            "type": "assignContext",
            "assign": {
              "user": "{{event}}"
            }
          }
        ],
        "else": [
          {
            "type": "toast",
            "message": "Failed to load user profile. Please try again."
          }
        ]
      }
    ]
  }
}

Testing Services

Test your services independently before integrating them into forms:

javascript
// Test REST service
const testService = {
  type: "rest",
  url: "/api/test",
  method: "GET"
};

// Test function service
function testCalculation(context) {
  return context.price * context.quantity;
}