Skip to main content
The HTTP Functions module enables you to register and invoke HTTP endpoints as native functions in your iii application. Perfect for integrating with external APIs, webhooks, and microservices.

Configuration

Configure the HTTP Functions module in config.yaml:
config.yaml
modules:
  - class: modules::http_functions::HttpFunctionsModule
    config:
      security:
        url_allowlist:
          - "https://api.example.com/*"
          - "https://*.trusted-domain.com/*"
        block_private_ips: true
        require_https: true

Security Configuration

The module includes built-in security controls to protect against SSRF and other vulnerabilities.
security.url_allowlist
array<string>
default:"['*']"
List of URL patterns allowed for HTTP function invocations. Supports wildcards.Examples:
  • https://api.example.com/* - Allow all endpoints under api.example.com
  • https://*.example.com/* - Allow all subdomains of example.com
  • * - Allow all URLs (use with caution)
security.block_private_ips
boolean
default:true
Block requests to private IP ranges (RFC 1918) to prevent SSRF attacks.Blocked ranges:
  • 10.0.0.0/8
  • 172.16.0.0/12
  • 192.168.0.0/16
  • 127.0.0.0/8
  • 169.254.0.0/16
security.require_https
boolean
default:true
Require HTTPS for all HTTP function invocations. Rejects HTTP URLs.
The default security settings (block_private_ips: true, require_https: true) are recommended for production deployments to prevent SSRF vulnerabilities.

Registering HTTP Functions

HTTP functions are registered dynamically at runtime using the register_http_function method:
import { HttpFunctionConfig } from 'iii';

// Register an HTTP endpoint as a function
const config: HttpFunctionConfig = {
  function_path: 'external.getUser',
  url: 'https://api.example.com/users/{userId}',
  method: 'GET',
  description: 'Fetch user details from external API',
  timeout_ms: 5000,
  headers: {
    'Content-Type': 'application/json',
    'X-API-Key': process.env.API_KEY,
  },
};

await httpFunctionsModule.register_http_function(config);

HttpFunctionConfig

function_path
string
required
The function ID to register. Used when calling the function via client.call().
url
string
required
The HTTP endpoint URL. Supports path parameters using {param} syntax.Must pass URL validation against the allowlist.
method
string
required
HTTP method: GET, POST, PUT, PATCH, DELETE
description
string
Optional description of the function’s purpose
timeout_ms
number
default:30000
Request timeout in milliseconds
headers
object
HTTP headers to include in the request. Common use cases:
  • Authentication: Authorization: Bearer {token}
  • Content type: Content-Type: application/json
  • API keys: X-API-Key: {key}
auth
HttpAuth
Authentication configuration. See Authentication section.
request_format
object
JSON schema for request validation
response_format
object
JSON schema for response validation
metadata
object
Arbitrary metadata attached to the function

Invoking HTTP Functions

Once registered, HTTP functions are called like any other function:
// Call the registered HTTP function
const result = await client.call('external.getUser', {
  userId: 123,
  expand: 'profile',
});

console.log(result);
// { id: 123, name: 'Alice', email: '[email protected]', ... }

Path Parameters

URL path parameters are extracted from the input data:
// URL: https://api.example.com/users/{userId}/posts/{postId}
const result = await client.call('external.getUserPost', {
  userId: 123,
  postId: 456,
});
// Makes request to: https://api.example.com/users/123/posts/456

Query Parameters

Non-path parameters are added as query strings:
// URL: https://api.example.com/users/{userId}
const result = await client.call('external.getUser', {
  userId: 123,
  expand: 'profile',
  fields: 'name,email',
});
// Makes request to: https://api.example.com/users/123?expand=profile&fields=name,email

Request Body

For POST/PUT/PATCH requests, the input is sent as the request body:
// URL: https://api.example.com/users
// Method: POST
const result = await client.call('external.createUser', {
  name: 'Bob',
  email: '[email protected]',
});
// Sends POST request with JSON body: { name: 'Bob', email: '[email protected]' }

Authentication

The module supports various authentication methods:

API Key in Headers

const config = {
  function_path: 'external.api',
  url: 'https://api.example.com/data',
  method: 'GET',
  headers: {
    'X-API-Key': process.env.API_KEY,
  },
};

Bearer Token

const config = {
  function_path: 'external.api',
  url: 'https://api.example.com/data',
  method: 'GET',
  headers: {
    'Authorization': `Bearer ${process.env.ACCESS_TOKEN}`,
  },
};

Basic Auth

const config = {
  function_path: 'external.api',
  url: 'https://api.example.com/data',
  method: 'GET',
  auth: {
    type: 'basic',
    username: 'user',
    password: 'pass',
  },
};

OAuth 2.0

const config = {
  function_path: 'external.api',
  url: 'https://api.example.com/data',
  method: 'GET',
  auth: {
    type: 'oauth2',
    token_url: 'https://auth.example.com/token',
    client_id: process.env.CLIENT_ID,
    client_secret: process.env.CLIENT_SECRET,
  },
};

Unregistering Functions

Remove HTTP functions when they’re no longer needed:
await httpFunctionsModule.unregister_http_function('external.getUser');

Use Cases

External API Integration

// Register multiple endpoints from the same API
await httpFunctionsModule.register_http_function({
  function_path: 'github.getUser',
  url: 'https://api.github.com/users/{username}',
  method: 'GET',
  headers: {
    'Authorization': `token ${process.env.GITHUB_TOKEN}`,
  },
});

await httpFunctionsModule.register_http_function({
  function_path: 'github.listRepos',
  url: 'https://api.github.com/users/{username}/repos',
  method: 'GET',
  headers: {
    'Authorization': `token ${process.env.GITHUB_TOKEN}`,
  },
});

// Use in your functions
export async function analyzeUser(input: { username: string }) {
  const user = await client.call('github.getUser', input);
  const repos = await client.call('github.listRepos', input);
  
  return {
    user,
    repoCount: repos.length,
    languages: repos.map(r => r.language),
  };
}

Microservice Communication

// Register internal microservices as functions
await httpFunctionsModule.register_http_function({
  function_path: 'auth.validateToken',
  url: 'http://auth-service:3000/validate',
  method: 'POST',
  timeout_ms: 2000,
});

await httpFunctionsModule.register_http_function({
  function_path: 'billing.createInvoice',
  url: 'http://billing-service:3000/invoices',
  method: 'POST',
});

Webhook Forwarding

// Forward webhook data to external service
await httpFunctionsModule.register_http_function({
  function_path: 'slack.sendMessage',
  url: 'https://hooks.slack.com/services/YOUR/WEBHOOK/URL',
  method: 'POST',
});

export async function onOrderCreated(data: any) {
  await client.call('slack.sendMessage', {
    text: `New order #${data.orderId} for $${data.amount}`,
  });
}

Error Handling

URL Validation Errors

// Fails if URL not in allowlist
await httpFunctionsModule.register_http_function({
  function_path: 'bad.function',
  url: 'https://blocked-domain.com/api',
  method: 'GET',
});
// Returns: { code: 'url_validation_failed', message: '...' }

Request Failures

const result = await client.call('external.api', { ... });

if (result.error) {
  console.error('HTTP request failed:', result.error);
  // Handle timeout, network errors, etc.
}

Security Best Practices

  1. Use allowlists - Restrict HTTP functions to trusted domains
  2. Enable HTTPS - Set require_https: true in production
  3. Block private IPs - Keep block_private_ips: true to prevent SSRF
  4. Rotate credentials - Store API keys in environment variables, not hardcoded
  5. Validate responses - Use response_format to validate external API responses
  6. Set timeouts - Prevent hanging requests with appropriate timeout_ms values
  7. Rate limiting - Implement rate limiting for external API calls
  8. Monitor usage - Track HTTP function invocations for unusual patterns

Production Configuration

config.yaml
modules:
  - class: modules::http_functions::HttpFunctionsModule
    config:
      security:
        # Strict allowlist for production
        url_allowlist:
          - "https://api.stripe.com/*"
          - "https://api.github.com/*"
          - "https://*.googleapis.com/*"
        
        # Prevent SSRF attacks
        block_private_ips: true
        
        # Enforce HTTPS
        require_https: true

Development Configuration

config.yaml
modules:
  - class: modules::http_functions::HttpFunctionsModule
    config:
      security:
        # Allow all URLs in development
        url_allowlist:
          - "*"
        
        # Allow localhost/private IPs for local services
        block_private_ips: false
        
        # Allow HTTP for local testing
        require_https: false
Never use the development configuration in production. Always enforce strict security controls.

Limitations

  1. Synchronous only - HTTP functions are invoked synchronously
  2. No streaming - Response must fit in memory
  3. No retries - Failed requests do not retry automatically
  4. No caching - Responses are not cached (implement caching in your code)

API Reference

HttpInvoker

The underlying HTTP invoker is accessible via:
const invoker = httpFunctionsModule.http_invoker();

Registered Functions

Access the registry of HTTP functions:
const functions = httpFunctionsModule.http_functions();
for (const [path, config] of functions.entries()) {
  console.log(`${path}: ${config.url}`);
}

Build docs developers (and LLMs) love