Skip to main content

REST and HTTP Operations

The iXML language provides facilities for building RESTful APIs and consuming HTTP services. REST operations cover two use cases: REST server for building APIs, and REST client for consuming external APIs. HTTP operations provide low-level request capabilities.

REST Server (Building APIs)

rest:server / rest:resource

rest:server defines a REST server with route-based dispatch. rest:resource defines individual endpoints.

Key concepts:

  • Route parameters use :name syntax (/users/:id)
  • Variadic routes use *name (/files/*path)
  • Query parameters are automatically extracted as local variables
  • Set HTTP status with <set var="return">404</set>
  • Each handler executes in its own local context — use <use> to import parent variables
XML
<rest:server>
<!-- GET /person/:id -->
<rest:resource route="/person/:id" method="GET">
<array:keyexists var="people" var_result="exists">$id</array:keyexists>

<is var="exists" type="false">
<set var="return">404</set>
<return/>
</is>

<header>Content-Type: application/json</header>
<output>
<encode:json var="people[$id]"/>
</output>
</rest:resource>

<!-- POST /person (JSON body) -->
<rest:resource route="/person" method="POST" var_body="body">
<try>
<decode:json var="data">$body</decode:json>

<if value1="$data.email" func="=" value2="">
<set var="return">400</set>
<header>Content-Type: application/json</header>
<output>{"error": "Email is required"}</output>
<return/>
</if>

<db:set entity="contacts" var="new_id" var_data="data"/>
<set var="return">201</set>
<header>Content-Type: application/json</header>
<output>{"result": 1, "id": $new_id}</output>

<catch var="error">
<set var="return">400</set>
<header>Content-Type: application/json</header>
<output>{"error": "Invalid JSON"}</output>
</catch>
</try>
</rest:resource>

<!-- DELETE /person/:id -->
<rest:resource route="/person/:id" method="DELETE">
<unset var="people[$id]"/>
<header>Content-Type: application/json</header>
<output>{"result": 1}</output>
</rest:resource>
</rest:server>

Accessing request data

XML
<rest:resource route="/protected" method="GET" var_header="headers" var_body="body">
<use var="database_connection"/>

<!-- Path parameters become local variables -->
<output>ID: $id</output>

<!-- Query parameters become local variables -->
<output>Search: $q</output>

<!-- Request headers via var_header -->
<output>Auth: $headers.Authorization</output>

<!-- Request body via var_body -->
<decode:json var="payload">$body</decode:json>
</rest:resource>
warning

Output Buffering: All output within rest:server is buffered. Do not send output before the rest:server block, as this will commit HTTP headers prematurely.


REST Client (Consuming APIs)

rest:client / rest:bind

rest:client establishes a connection context for an external API. rest:bind creates callable functions bound to endpoints.

XML
<rest:client url="https://api.predic8.de/shop/v2" timeout="30">
<rest:header>
Content-Type: application/json
Accept: application/json
</rest:header>

<rest:bind var="createProduct" method="POST">/products/</rest:bind>
<rest:bind var="updateProduct" method="PATCH">/products/:id</rest:bind>
<rest:bind var="getProduct" method="GET">/products/:id</rest:bind>
<rest:bind var="deleteProduct" method="DELETE">/products/:id</rest:bind>
</rest:client>

<!-- Create a product -->
<call func="createProduct" var="response">
<param>{"name": "Wildberries", "price": 4.99}</param>
</call>

<decode:json var="product">$response</decode:json>
<output>Created product ID: $product.id</output>

<!-- Update the product -->
<call func="updateProduct">
<param>$product.id</param>
<param>{"price": 5.99}</param>
</call>

Parameter passing:

  • Path parameters: first N params fill :param placeholders in order
  • Request body: remaining params become the body (POST, PUT, PATCH)
  • Use type="raw" on rest:bind to get full response with status, type, header, body

HTTP Operations

http:request

http:request sends a low-level HTTP request with full control over headers, body, and response handling.

XML
<!-- GET request -->
<http:request url="https://api.example.com/users/123" var="response"/>

<!-- POST with JSON -->
<http:request url="https://api.example.com/users" method="POST" var="response" var_info="info">
<http:header>
Content-Type: application/json
Authorization: Bearer $api_token
</http:header>
<http:body>
{"name": "Jane Doe", "email": "[email protected]"}
</http:body>
</http:request>

<!-- Check response status -->
<if value1="$info.status" func="=" value2="200">
<decode:json var="user">$response</decode:json>
<output>User: $user.name</output>
<else/>
<output>Error: $info.status</output>
</else></if>

The var_info array contains: status (HTTP code), type (Content-Type), header (raw headers), body (response body).

http:urlinfo

http:urlinfo parses a URL into its components (scheme, host, port, path, query, fragment, args):

XML
<http:urlinfo var="info" url="https://example.com:8080/api/users?role=admin"/>
<output>Host: $info.host, Path: $info.path, Role: $info.args.role</output>

http:query

http:query builds a URL-encoded query string from an associative array:

XML
<array var="params">
<set var="search">iXML programming</set>
<set var="limit">10</set>
</array>

<http:query var="params" var_result="qs"/>
<output>$qs</output>
<!-- search=iXML+programming&limit=10 -->

Response patterns

Consistent JSON envelope

XML
<header>Content-Type: application/json</header>

<!-- Success -->
<output>{"result": 1, "data": <encode:json var="payload"/>}</output>

<!-- Error -->
<set var="return">400</set>
<output>{"error": "Validation failed", "details": "$message"}</output>

HTTP status codes

CodeWhen to use
200Successful GET, PUT, PATCH, DELETE
201Successful POST creating a resource
400Invalid input, malformed JSON
401Missing or invalid authentication
404Resource not found
500Unexpected server error