HTTP request trigger
An HTTP request function turns a URL on your Raytha site into a JavaScript endpoint. You write get(query) and post(payload, query), return a result helper, and Raytha sends the response. This page is the reference for the request shapes, the helpers, the URLs and the failure modes.
A working endpoint
Create a function with the trigger HTTP request, the developer name greet, and mark it active:
function field(payload, name) {
if (Array.isArray(payload)) { // form post
var entry = payload.find(function (i) { return i.Key === name; });
return entry && entry.Value.length > 0 ? entry.Value[0] : "";
}
return payload && payload[name] !== undefined ? String(payload[name]) : ""; // JSON post
}
function post(payload, query) {
var name = field(payload, "name");
if (!name) {
return new StatusCodeResult(400, "name is required");
}
return new JsonResult({ received: name });
}
Call it with JSON, then with a form. Both reach the same post():
curl -s -X POST "$RAYTHA_URL/raytha/functions/execute/greet" \
-H "Content-Type: application/json" \
-d '{"name":"Ada"}'
curl -s -X POST "$RAYTHA_URL/raytha/functions/execute/greet" \
-d "name=Ada"
{
"received": "Ada"
}
URLs
| URL | Notes |
|---|---|
/raytha/functions/execute/{developerName} | Always available for an active HTTP function. Anonymous. No antiforgery token needed. |
/{route path} | Optional. Set a route path on the function, for example sitemap.xml or api/contact, and the function answers there through the public site's catch-all route. If the function is inactive or missing, the visitor sees the site's 404 page. |
Only GET, HEAD and POST run a function. HEAD runs get(). Any other method gets 405 with the body Functions answer GET, HEAD, and POST. and an Allow: GET, HEAD, POST header.
A route path may be up to 200 characters. It may contain letters, digits, _, ., / and -. It cannot start or end with /, a dot is allowed only in the last segment, and paths under reserved roots such as raytha and account are rejected. It must also be unique among all routes, including content items and site pages.
get(query)
Called for GET and HEAD. query is an array with one entry per query string key. Each entry has Key and Value, and Value is an array of strings, because a key can repeat. The key names are case-sensitive.
[
{ "Key": "page", "Value": ["3"] },
{ "Key": "tag", "Value": ["a", "b"] }
]
This is the request ?page=3&tag=a&tag=b. A helper keeps the code readable:
// query and form payloads arrive as [{ Key: "name", Value: ["a", "b"] }, ...]
function param(list, name, fallback) {
var entry = list.find(function (i) { return i.Key === name; });
return entry && entry.Value.length > 0 ? entry.Value[0] : fallback;
}
function get(query) {
var page = parseInt(param(query, "page", "1"), 10);
return new JsonResult({ page: page, q: param(query, "q", "") });
}
post(payload, query)
Called for POST. query has the same shape as in get(). The shape of payload depends on the request's Content-Type:
| Request body | payload |
|---|---|
application/x-www-form-urlencoded or multipart/form-data | An array of { Key, Value: [strings] }, the same shape as query. Uploaded files are not passed. |
Anything else, including application/json | A JSON body becomes a JavaScript value: an object, array, string, number, boolean or null. An empty body is null. Any other text arrives as a string. |
Raytha reads a non-form body as text and parses it as JSON before your code runs. The text is never inserted into the script, so a body cannot run as code. An empty body is null. A body that is not JSON is the raw text, as a string, and your function decides what to do with it.
post() may be async and return a promise. A promise that never settles ends at the timeout.
Result helpers
Return exactly one helper. All of them are defined before your code runs.
| Helper | Status | Content-Type | Body |
|---|---|---|---|
new JsonResult(value) | 200 | application/json | The value as indented JSON. A JavaScript object keeps your property names. A .NET object from API_V1 is written with PascalCase names. |
new HtmlResult(html) | 200 | text/html | The string. |
new XmlResult(xml) | 200 | application/xml | The string. |
new TextResult(text) | 200 | text/plain; charset=utf-8 | The string. |
new ContentResult(body, contentType, statusCode) | Default 200, allowed 200 to 599 | Default text/plain; charset=utf-8, any valid media type | The string. Use it for CSV, RSS, application/xml; charset=utf-8, text/calendar and anything else. |
new StatusCodeResult(statusCode, message) | 200 to 599 | text/plain for a string message | The message. |
new RedirectResult(url) | 302 | A Location header with the url. Relative and absolute URLs work. |
function get(query) {
var kind = query.length > 0 ? query[0].Value[0] : "json";
switch (kind) {
case "json": return new JsonResult({ ok: true });
case "text": return new TextResult("plain text");
case "html": return new HtmlResult("<h1>Hello</h1>");
case "xml": return new XmlResult("<a><b/></a>");
case "content": return new ContentResult("a,b\n1,2\n", "text/csv; charset=utf-8", 200);
case "status": return new StatusCodeResult(404, "No such thing");
case "redirect": return new RedirectResult("/pricing");
}
return new StatusCodeResult(400, "unknown kind");
}
These cases return a 500 with application/problem+json and the title Invalid function result:
- The function returns nothing,
undefinedornull. - The function returns a bare value such as an object or a string.
- A
ContentResulthas a content type that does not parse, or one containing a line break. - A status code is outside 200 to 599.
- A
RedirectResulturl is empty or contains a line break.
You cannot set response headers or cookies. Raytha sends no CORS headers from functions, so a script on another origin cannot read the response. A plain HTML form on another site can still post to the function.
Errors and timeouts
| Situation | Response |
|---|---|
| Function does not exist or is inactive | 404. Via a route path: the site's 404 page. |
| Method other than GET, HEAD, POST | 405 with an Allow header. |
| Script throws or has a syntax error | 500. The body is the error text with the failing line, as plain text. |
Script runs longer than RAYTHA_FUNCTIONS_TIMEOUT | 500 with a timeout message. The engine interrupts the script, and any HttpClient request it started is cancelled. |
No free slot within RAYTHA_FUNCTIONS_QUEUE_TIMEOUT | 503 "The server is too busy". |
| Invalid return value | 500 problem+json, Invalid function result. |
A thrown error exposes the message and code line to the caller. Catch errors that carry internal detail:
function get(query) {
try {
var res = API_V1.GetContentItemById("not-a-valid-id");
return new JsonResult({ ok: res.Success });
} catch (e) {
return new StatusCodeResult(400, "Bad request");
}
}
An id that is not a valid 22-character ShortGuid throws before any query runs, and so does an id that does not exist. Validation failures, such as a bad field value, do not throw: the response has Success === false and the message in Error. Check Success after every write.
A form that posts to a function
<form method="post" action="/raytha/functions/execute/contact">
<input name="name" required>
<input name="email" type="email" required>
<textarea name="message" required></textarea>
<!-- Honeypot: hide this input with CSS. Real visitors never fill it in. -->
<input name="website" tabindex="-1" autocomplete="off">
<button>Send</button>
</form>
The full handler for this form is in Recipes.
Gotchas
- The function sees no headers. There is no way to read
Authorization,User-Agent, cookies or a signature header. Put secrets in the query string and compare them in code.CurrentUser.RemoteIpAddressgives the client address. - The body is not raw. You get the parsed value, not the original bytes, so you cannot recompute a signature over it.
- Form values are arrays.
payload[0].Valueis an array, so read.Value[0]. - Don't return an
API_V1response directly in public endpoints. It exposes internal fields and ids as{"Guid":..., "Value":...}objects. Build the JSON you want. - Globals reset. Each call runs in a fresh engine. Use content items for state.
Next steps
- Built-in objects:
API_V1,HttpClient,Emailerand the rest. - Recipes: working handlers you can copy.
- Webhook trigger: receive and send webhooks.
- Raytha Functions: limits and security.