7.3 KiB
Endpoints
Endpoints are REST WebServices written in ES5 JavaScript, executed on the Rhino engine. Each file must export a single WebService instance. Vitruvio maps incoming HTTP requests to the corresponding handler method.
vitruvio.json Registration
{
"key": "my-endpoint",
"name": "My Endpoint",
"description": "What this endpoint does.",
"language": "javascript",
"authMode": "PUBLIC",
"active": true,
"source": "endpoints/my-endpoint.js"
}
key— kebab-case, stable. Changing it breaks any external integrations pointing to the URL.authMode— one of"PUBLIC","STATIC_TOKEN","VITRUVIO_WS_USER_AUTH","HTTP_BASIC_AUTH". Default to"PUBLIC".active— must betruefor the endpoint to be reachable. Set tofalseto disable without removing.
URL structure
The authMode determines the URL segment used to reach the endpoint:
| authMode | URL |
|---|---|
PUBLIC |
/api/integration/public/{key} |
STATIC_TOKEN |
/api/integration/tokenauth/{key} |
VITRUVIO_WS_USER_AUTH |
/api/integration/bearerauth/{key} |
HTTP_BASIC_AUTH |
/api/integration/bauth/{key} |
STATIC_TOKEN authentication
The token can be sent in either of two ways:
- Query parameter:
_x_token_auth=<token> - Request header:
X-WS-TOKEN-AUTH: <token>
File Structure
function WebService() {
this.onGet = function(params) { ... }
this.onPost = function(params) { ... }
this.onPut = function(params) { ... }
this.onPatch = function(params) { ... }
this.onDelete = function(params) { ... }
}
module.exports = new WebService();
- All methods are optional. Only implement the verbs your endpoint actually handles.
- Remove unused verb stubs entirely — don't leave placeholder
return JSON.stringify({ prop: value })bodies in production code. module.exportsmust benew WebService()— Vitruvio instantiates and reads from this exported object.- If a caller sends a verb that is not implemented in the script, Vitruvio throws
EndpointMethodNotImplementedException— it is not a silent 404.
The params Object
Each handler receives a single params argument:
// GET / DELETE
params = {
headers: {}, // request headers as key/value
query: {} // query string params as key/value e.g. params.query["hub.mode"]
}
// POST / PUT / PATCH
params = {
requestBody: '', // raw request body string — always JSON.parse() before use
headers: {},
query: {}
}
Always parse the body explicitly:
this.onPost = function(params) {
var body = JSON.parse(params.requestBody);
// use body.*
}
Return Values
| Return value | HTTP response |
|---|---|
| A non-empty string | 200 OK with that string as body |
null, no return, or empty string "" |
204 No Content |
JSON.stringify(obj) |
200 OK with JSON body — set Content-Type accordingly |
Always return strings. Returning a raw object will not serialize correctly.
// Correct
return JSON.stringify({ status: 'ok', data: result });
// Wrong — object will not be serialized properly
return { status: 'ok' };
Error Handling
Wrap handler logic in try/catch and return a JSON error body. Don't let uncaught exceptions bubble up.
this.onPost = function(params) {
try {
var body = JSON.parse(params.requestBody);
if (!body.id) throw "Missing required field: id";
// ...
return JSON.stringify({ success: true });
} catch (e) {
return JSON.stringify({ error: e.toString() });
}
};
- Use
e.toString()when serializing caught exceptions — raw exception objects may not stringify correctly under Rhino. - Validate required fields early and throw descriptive messages.
DELETE Convention
By convention, DELETE requests should not receive a body and should not return data. If nothing is returned, Vitruvio responds with 204 No Content.
this.onDelete = function(params) {
// perform deletion
// no return needed — 204 is implied
}
Loading Scripts and Libraries
Use libService.loadScript(key) to load scripts registered in vitruvio.json. The key is the script's key field in the manifest.
// Load inside the handler — safe, always fresh
this.onPost = function(params) {
var lib = libService.loadScript('my-lib');
lib.doSomething();
}
// Load in the constructor — shared across all calls to this endpoint instance
function WebService() {
var lib = libService.loadScript('my-lib');
this.onPost = function(params) {
lib.doSomething();
}
}
Prefer constructor-level loading when the same library is used in multiple handlers — avoids redundant loads. Use handler-level loading when the script key is dynamic or determined at runtime.
Database Access
Load the db library and instantiate with a datasource name:
var db = libService.loadScript('db');
var banco = new db(db.VITRUVIO_DATASOURCE); // default Vitruvio datasource
// or
var banco = new db('named-datasource');
Common operations:
// Single row
var row = banco.queryRow("SELECT NAME FROM TB_CONFIG WHERE ID = :id", { id: 1 });
// Multiple rows
var rows = banco.query("SELECT * FROM TB_ITEMS WHERE ACTIVE = 1");
// Update / Insert
banco.update("UPDATE TB_ITEMS SET STATUS = :status WHERE ID = :id", {
status: 'DONE',
id: itemId
});
- Prefer named queries from
queries/*.sqlfor reusable or complex SQL. Use inline SQL only for simple, endpoint-specific queries. - Use named bind parameters (
:paramName) — never concatenate user input into SQL strings.
Logged User (Authenticated Modes)
For VITRUVIO_WS_USER_AUTH and HTTP_BASIC_AUTH, Vitruvio resolves the caller's identity and injects a loggedUser object into the script context. This gives the endpoint access to who is making the call.
HTTP_BASIC_AUTH additionally validates that the authenticated user belongs to the roles configured for that endpoint in Vitruvio's admin.
Common Patterns
Webhook with verification (e.g. Meta/WhatsApp)
this.onGet = function(params) {
var mode = params.query["hub.mode"];
var token = params.query["hub.verify_token"];
var challenge = params.query["hub.challenge"];
if (mode === "subscribe" && token === EXPECTED_TOKEN) {
return challenge;
}
return JSON.stringify({ error: "Forbidden" });
};
Delegating to a script
this.onPost = function(params) {
try {
var body = JSON.parse(params.requestBody);
var lib = libService.loadScript('my-processing-script');
var result = lib.run(body);
return JSON.stringify(result);
} catch (e) {
return JSON.stringify({ error: e.toString() });
}
};
What NOT to do
- Don't leave placeholder bodies (
return JSON.stringify({ prop: value })) in unused verb stubs — remove the whole method instead. - Don't return raw exception objects — always call
.toString()or wrap in a message. - Don't hardcode datasource names or tokens in the script — read them from
vConfigServiceor from a DB config table. - Don't put heavy business logic directly in the endpoint file — delegate to a script via
libService.loadScript.