253 lines
7.3 KiB
Markdown
253 lines
7.3 KiB
Markdown
# 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
|
|
|
|
```json
|
|
{
|
|
"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 be `true` for the endpoint to be reachable. Set to `false` to 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
|
|
|
|
```javascript
|
|
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.exports` must be `new 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:
|
|
|
|
```javascript
|
|
// 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:**
|
|
```javascript
|
|
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.
|
|
|
|
```javascript
|
|
// 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.
|
|
|
|
```javascript
|
|
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`.
|
|
|
|
```javascript
|
|
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.
|
|
|
|
```javascript
|
|
// 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:
|
|
|
|
```javascript
|
|
var db = libService.loadScript('db');
|
|
var banco = new db(db.VITRUVIO_DATASOURCE); // default Vitruvio datasource
|
|
// or
|
|
var banco = new db('named-datasource');
|
|
```
|
|
|
|
Common operations:
|
|
|
|
```javascript
|
|
// 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/*.sql` for 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)
|
|
```javascript
|
|
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
|
|
```javascript
|
|
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 `vConfigService` or from a DB config table.
|
|
- Don't put heavy business logic directly in the endpoint file — delegate to a script via `libService.loadScript`.
|