Kora/developer documentation/Routes

Routes

API endpoints for your module, with permissions, rate limits and signed responses.

A route is an endpoint on the API port. Put each one in its own folder under src/routes/ as route.js.

modules/example/src/routes/items/route.js
import { Routes } from "#kora/modules";

export class ItemsRoute extends Routes {
    constructor(kora) {
        super(kora, {
            name: "items",
            path: "/items",
            permissions: { GET: "example.view", POST: "example.edit" }
        });
    }

    async GET(request) {
        const items = this.kora.modules.models.get(this.module, "example_items");
        return items.page(items.query(request.query));
    }

    async POST(request) {
        const created = await this.kora.modules.handlers.get(this.module, "items").create(request.body.identifier, request.body.name);
        await this.audit(request, "items.create", created.id);
        return created;
    }
}

This answers GET and POST on /api/modules/example/items.

Add a method for each HTTP verb you need: GET, POST, PUT, PATCH or DELETE. Whatever it returns is sent as JSON. Returning nothing sends 204. A thrown error with a status, such as from fail(), is sent as that status with { error }.

Options

KeyDefaultWhat it does
nameRequiredThe route name.
pathRequiredThe path. Supports Express parameters such as /items/:id.
absolutefalseMount path as is instead of under /api/modules/<module>. Use it for public /api/v1/ endpoints.
aliases[]Extra full paths for the same route.
accessprivatepublic lets anyone call it. private needs a dashboard session or an API token.
permissions{}A permission for every method, or one per method.
limitNoneRate limit per IP, such as { window: 60, max: 30 } for 30 a minute.
rawfalseSkip JSON body parsing, for webhooks that need the raw body.

When the module is turned off, its routes answer 404.

Helpers

HelperWhat it does
this.can(request, permission)Whether the caller has the permission.
this.require(request, permission)Throws 403 if they do not.
this.actor(request)Who is calling. kind is staff, token, public or system.
this.ip(request)The caller's IP, behind proxies too.
this.audit(request, action, target, detail)Adds an entry to the audit log.
this.signed(response, data, status)Sends data with an x-kora-signature header.
this.listing(request, name, page)Wraps a page() result as { success, total, [name] } on /api/v1/ paths.

Signed Responses

Anything your product reads to make a licensing decision should be signed, so it cannot be faked:

async POST(request, response) {
    this.signed(response, { valid: true, timestamp: Date.now() });
}

signed sends the response itself, so return nothing after it. The SDKs check the header with the public key. See Signed Responses.