Appearance
HTTP Ingress Handlers
HTTP ingress rules map an HTTP method + URL path to a JavaScript script. When a matching request arrives, DieselEngine creates a fresh GraalVM context, evaluates your script as an ES module, and calls the exported handleRequest function.
How It Works
- A request arrives at a URL matching an ingress rule (e.g.,
GET /api/items) - The engine loads the linked script and creates a new GraalVM context
- All global bindings (
diesel,Results,okHttpClient, etc.) are injected - The script's
handleRequest(context)export is called - The returned
IngressResultis sent as the HTTP response - The GraalVM context is closed
INFO
Each HTTP request gets its own isolated GraalVM context. There is no shared state between requests unless you use the database, Redis, or the registry.
Ingress Types
| Type | HTTP Method | Use Case |
|---|---|---|
HTTP_GET | GET | Read operations, queries |
HTTP_POST | POST | Create/update operations |
HTTP_DELETE | DELETE | Delete operations |
Basic Handler
Every HTTP ingress script must export a handleRequest function that receives a RequestContext and returns a Results chain:
javascript
export function handleRequest(context) {
return Results.json().renderRaw(JSON.stringify({
message: "Hello from DieselEngine!",
timestamp: Date.now()
}));
}URL Path
All HTTP ingress routes are served behind the /backend path prefix (added by the Caddy reverse proxy in the standard Docker setup). So an ingress with route /api/items is accessible at:
http://your-host/backend/api/itemsReading Parameters
Query Parameters (GET)
javascript
export function handleRequest(context) {
const page = context.getParameterAsInteger("page") || 1;
const limit = context.getParameterAsInteger("limit") || 20;
const search = context.getParameter("q") || "";
const items = ItemService.findItems(search, page, limit);
return Results.json().renderRaw(JSON.stringify({ data: items, page, limit }));
}Request Body (POST)
javascript
export function handleRequest(context) {
const body = JSON.parse(context.getBody());
const { name, email, role } = body;
if (!name || !email) {
return Results.badRequest().json().renderRaw(
JSON.stringify({ error: "name and email are required" })
);
}
const id = UserService.createUser({ name, email, role });
return Results.created("/api/users/" + id)
.json()
.renderRaw(JSON.stringify({ id }));
}Path-Based Routing
DieselEngine routes are exact matches. To handle paths like /api/items/123, use query parameters (/api/items?id=123) or parse the path manually:
javascript
export function handleRequest(context) {
const path = context.getRequestPath();
const segments = path.split("/");
const id = segments[segments.length - 1];
// ...
}The Delegation Pattern
Keep handlers thin. Extract parameters, delegate to a service class, and format the response:
javascript
import { ItemService } from '../../services/ItemService.js';
export function handleRequest(context) {
const id = context.getParameterAsInteger("id");
if (!id) {
return Results.badRequest().json().renderRaw(
JSON.stringify({ error: "Missing 'id' parameter" })
);
}
const item = ItemService.getItemById(id);
if (!item) {
return Results.notFound().json().renderRaw(
JSON.stringify({ error: "Item not found" })
);
}
return Results.json().renderRaw(JSON.stringify(item));
}The service class encapsulates all database access:
javascript
// /services/ItemService.js
export class ItemService {
static getItemById(id) {
const ps = diesel.prepareStatement(
'SELECT id, data, created_at FROM engine."items" WHERE id = ? AND deleted = false'
);
ps.setLong(1, id);
const rs = ps.executeQuery();
if (!rs.next()) return null;
return {
id: rs.getLong("id"),
...JSON.parse(rs.getString("data")),
created_at: rs.getString("created_at")
};
}
}Auth Patterns
Bearer Token
javascript
export function handleRequest(context) {
const auth = context.getHeader("Authorization");
if (!auth || !auth.startsWith("Bearer ")) {
return Results.unauthorized().json().renderRaw(
JSON.stringify({ error: "Missing or invalid token" })
);
}
const token = auth.substring(7);
const user = AuthService.validateToken(token);
if (!user) {
return Results.forbidden().json().renderRaw(
JSON.stringify({ error: "Invalid token" })
);
}
// Proceed with authenticated user...
return Results.json().renderRaw(JSON.stringify({ user }));
}Cookie / Session
javascript
export function handleRequest(context) {
const cookie = context.getHeader("Cookie");
const sessionId = parseCookie(cookie, "session_id");
// ...
}File Uploads
Handle multipart file uploads using the file item iterator:
javascript
export function handleRequest(context) {
const iterator = context.getFileItemIterator();
while (iterator.hasNext()) {
const item = iterator.next();
if (!item.isFormField()) {
const fieldName = item.getFieldName();
const fileName = item.getName();
const stream = item.openStream();
// Upload to MinIO, process, etc.
}
}
return Results.json().renderRaw(JSON.stringify({ uploaded: true }));
}Error Handling
Script exceptions are caught by the engine. If your handler throws, the engine returns HTTP 500 and pushes the stack trace to the console. For controlled error responses, use try/catch:
javascript
export function handleRequest(context) {
try {
const data = JSON.parse(context.getBody());
const result = processData(data);
return Results.json().renderRaw(JSON.stringify(result));
} catch (e) {
console.error("Handler error:", e.message);
return Results.internalServerError().json().renderRaw(
JSON.stringify({ error: "Internal error", detail: e.message })
);
}
}Response Patterns
Redirect
javascript
return Results.redirect("/dashboard"); // 303 See Other
return Results.redirectTemporary("/maintenance"); // 307 Temporary RedirectNo Content
javascript
// DELETE handler
return Results.noContent(); // 204Custom Headers
javascript
return Results.json()
.addHeader("X-Total-Count", String(total))
.addHeader("Cache-Control", "public, max-age=300")
.renderRaw(JSON.stringify(data));Non-JSON Content
javascript
// CSV export
return Results.ok()
.contentType("text/csv")
.addHeader("Content-Disposition", "attachment; filename=export.csv")
.renderRaw(csvContent);
// HTML page
return Results.html().render(htmlString);
// Plain text
return Results.text().renderRaw("OK");