sercrod

Network Contracts

Sercrod network directives make the frontend/server contract visible in HTML attributes. This page focuses on the response shapes and trigger timing that must stay aligned with templates.

For directive-level behavior, see Communication.

Trigger Timing

*fetch and *api can run automatically or as user actions depending on the element.

On a non-clickable element, Sercrod treats the directive as initial loading.

<serc-rod data='{"items":[]}' *fetch="/api/items.json" *response="'items'">
    <ul>
        <li *for="item of items">%item.title%</li>
    </ul>
</serc-rod>

On a clickable element, Sercrod treats the directive as an action.

<serc-rod data='{"items":[]}'>
    <button type="button" *fetch="/api/items.json" *response="'items'">
        Load items
    </button>

    <ul>
        <li *for="item of items">%item.title%</li>
    </ul>
</serc-rod>

For *fetch and *api, clickable timing is decided by element tag/type: button, a without download, and button-like input elements. *fetch also treats input[type=image] as clickable. role="button" and tabindex="0" do not change an auto-running element into a click-triggered one.

If the visual design needs a paragraph, list item, or other wrapper, keep the wrapper decorative and place the directive on the actual trigger element:

<p class="load-control">
    <button type="button" *fetch="/api/items.json" *response="'items'">
        Load items
    </button>
</p>

*fetch Destination

*fetch uses *response to place the fetched JSON in host data.

<serc-rod data='{"items":[]}'>
    <div *fetch="/api/items.json" *response="'items'"></div>

    <ul>
        <li *for="item of items">%item.title%</li>
    </ul>
</serc-rod>

The response is stored in items. Because the template loops with *for="item of items", items must be an array.

Do not change the server response root without updating the template paths that read it.

*api With *response

*api often uses *response to place the response in a named data key.

<serc-rod data='{"form":{"email":"","message":""},"result":null}'>
    <input type="email" *input="form.email">
    <textarea *input="form.message"></textarea>

    <button
        type="button"
        *api="/api/contact.php"
        method="POST"
        body="form"
        *response="'result'"
    >Send</button>

    <p *if="result" *print="result.message"></p>
</serc-rod>

Because the template reads result.message, the endpoint should return:

{
    "ok": true,
    "message": "Submission received.",
    "errors": [],
    "received": {
        "email": "user@example.com",
        "message": "Hello"
    }
}

If the endpoint returns { "data": { "message": "..." } }, the template must read result.data.message instead. The template and response shape are a single contract.

Request Body Contract

When body="form" or payload="form" is used, the expression is evaluated in the Sercrod scope and JSON-serialized for non-GET requests.

<button
    type="button"
    *api="/api/contact.php"
    method="POST"
    body="form"
    *response="'result'"
>Send</button>

For non-GET JSON requests, Sercrod sends a request envelope. The server should read the user payload from the envelope's data field.

Choosing *fetch, *post, And *api

For type-then-submit flows, use *lazy on text inputs when parent template refresh may interfere with the user's next click. See Input Timing.