JSON & HTTP
XCX 4.4.1 JSON and HTTP
JSON
JSON objects in XCX 4.4.1 are mutable.
Creation
Values in the literal can be placeholders ("", 0, false) to be filled later via .set().
A <<< ... >>> literal produces a json value in every expression position - declarations, reassignments, and call arguments alike:
The literal's content must be valid JSON (strict or relaxed, the same acceptance as json.parse()). Content that is not valid JSON - for example embedded expressions, which the language does not support - keeps plain-string behavior.
Serialization from Collections
You can also create JSON objects and arrays directly from XCX collections using the .toJson() method. This is available for:
- Maps: Returns a JSON object.
- Tables: Returns a JSON array of objects.
See the Collections Documentation for more details on mapping and behavior.
Parsing
CAUTION:
Panic on Invalid JSON (R305): If parsing fails, the VM terminates immediately. Verify string content before parsing.
[!NOTE]
No nesting-depth limit: since XCX 4.3, json.parse() accepts arbitrarily deep nesting (the former 128-level recursion limit is lifted); the practical ceiling is available memory. Only malformed content triggers R305.
Mutability Pattern
Declare a schema with zero-values, then populate:
JSON Methods
| Method | Signature | Returns | Description |
|---|---|---|---|
.exists(path) | (s) → b | b | Checks if path exists and is non-null |
.get(path/idx) | (s/i) → json | json | Gets element at path or index |
.bind(path, var) | (s, ref) → b | b | Extracts value into a pre-declared XCX variable |
.set(path, val) | (s, T) → b | b | Sets value at path; creates key if missing |
.push(val) | (json) → b | b | Appends element to a JSON array node |
.size() / .count() / .len() | () → i | i | Interchangeable aliases; number of keys (object) or elements (array) |
.keys() | () → array:s | array:s | Returns an array of keys for a JSON object |
.toStr() | () → s | s | Serializes to JSON string |
.inject(path, map, tbl) | (s, map, table) → b | b | Bulk import of JSON array into XCX table |
.first() | () → json | json | Returns the first element of a JSON array; halt.error if empty |
NOTE:
.push() on JSON arrays: .push() works exclusively on JSON nodes that are arrays ([]). Calling .push() on a JSON object ({}) results in a halt.error.
[!IMPORTANT]
.bind() syntax: The second argument must be a previously declared variable. You cannot declare the type inline.
Path Notation
Both dot-notation and bracket notation are supported for nested access:
.inject() - Bulk Import
Import a JSON array directly into an XCX table:
JSON in HTTP Requests
Inside HTTP handlers, json: req has this structure:
HTTP Client
High-Level API
| Method | Signature | Returns |
|---|---|---|
net.get(url) | (s) → json | json |
net.post(url, body) | (s, json) → json | json |
net.put(url, body) | (s, json) → json | json |
net.delete(url) | (s) → json | json |
Response Object
| Field | Type | Description |
|---|---|---|
status | i | HTTP status code |
ok | b | true when status >= 200 and < 300 |
body | json | Parsed response body (if JSON); otherwise null |
headers | json | Response headers |
text | s | Response body as raw string (always available) |
error | s | Error message if request failed; empty string if OK |
ok is true when status >= 200 and < 300. Always check ok before accessing body.
Low-Level Builder (net.request)
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
method | s | Yes | - | "GET", "POST", "PUT", etc. |
url | s | Yes | - | Full URL with scheme |
headers | map:s<->s | No | {} | Additional request headers |
body | json | No | null | Ignored for GET and DELETE |
timeout | i | No | 10000 | Milliseconds |
HTTP Server
serve Directive
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
port | i | Yes | - | Port number (1–65535) |
host | s | No | "127.0.0.1" | "0.0.0.0" = all interfaces |
workers | i | No | 1 | Reserved: accepted but currently unused - handler execution is serialized |
routes | route list | Yes | - | Checked top-to-bottom, first-match wins |
NOTE:
serve: is a terminal statement. No code after this directive will execute.
Wildcard * matches any method or path - always place it last.
Handler Fibers
Every handler must be a fiber with the signature fiber name(json: req -> json):
A handler that does not call yield net.respond(...) results in an automatic 500 Internal Server Error.
net.respond()
| Parameter | Type | Required | Description | |
|---|---|---|---|---|
| status | i | Yes | HTTP status code | |
| body | `json \ | s` | Yes | JSON object or raw string; use <<< {} >>> for empty JSON |
| headers | map:s<->s | No | Additional response headers |
CORS and Preflight
By default, the XCX engine provides automatic CORS support by adding these headers if they are missing from the response:
Access-Control-Allow-Origin: *Access-Control-Allow-Methods: GET, POST, OPTIONS, DELETE, PATCHAccess-Control-Allow-Headers: Content-Type, Authorization, X-CSRF-TOKEN
TIP:
Recommended Practice: While the engine provides defaults, it is highly recommended to explicitly declare these headers in your code. This ensures your application remains predictable across different environments and clearly documents its security policy.
For preflight (OPTIONS) support, use a dedicated handler to explicitly set allowed methods and headers:
Concurrent Requests
Fibers allow overlapping I/O:
Security Constraints
| Constraint | Behavior |
|---|---|
localhost / 127.0.0.1 / ::1 | Allowed |
169.254.x.x (link-local) | halt.fatal - SSRF protection |
10.x, 172.16.x – 172.31.x, 192.168.x | halt.error - private ranges blocked |
file:// URLs | halt.fatal |
| Max response body size | 50 MB - a larger response returns status: 413, ok: false, error: "Body too large" |
| Incoming request body | No size limit is enforced; validate input size in handlers |