Kora/Overview

Overview

How a Kora module is laid out, how it loads and what it can hook into.

Everything Kora does at runtime comes from a module, including Core. Your own modules load the same way and get the same access to the database, API, dashboard, audit log and languages.

For installing and running Kora, see the Setup Guide.

Layout

Each folder in modules/ is one module. Kora imports its main.js and loads everything under src/ and resources/ by folder.

modules/
  example/
    main.js
    resources/
      config/
      dashboard/
        pages/
      lang/
    src/
      clocks/
      commands/
      events/
      handlers/
      models/
      routes/
FolderFiles loadedBase class
main.jsThe moduleModules
src/models/Every *.jsModels
src/handlers/Every *.jsHandlers
src/routes/Every route.js, at any depthRoutes
src/clocks/Every *.jsClocks
src/events/Every *.jsEvents
src/commands/Every *.jsCommands
resources/config/Every *.jsConfigs
resources/lang/en/*.js classes, plus a folder of JSON per localeLangs
resources/dashboard/pages/Every page.js, at any depthPages

Import every base class from #kora/modules.

Loading

Kora loads modules in two passes.

  1. Register. Install any missing dependencies.node packages, then read configs, languages, models, routes and pages.
  2. Load. Start handlers, events, commands and clocks, call onStartup(), sync tables and mount routes.

When Kora stops or a module is turned off, it stops clocks, removes events, calls onShutdown() and unmounts routes.

A module that throws while loading is marked as failed and shown on the Modules page. The others keep running.

main.js

modules/example/main.js
import { Modules } from "#kora/modules";

export class Example extends Modules {
    constructor(kora) {
        super(kora, {
            name: "example",
            displayName: "Example",
            author: "Your Name",
            version: "1.0.0",
            description: "Does one thing well",
            icon: "box",
            requires: ["core"],
            dependencies: { node: ["nanoid"] }
        });
    }

    async onStartup() {
        this.kora.console.log(this.kora.console.levels.info, `${this.getName()} started.`);
    }
}
KeyDefaultWhat it does
nameRequiredThe module ID. Use the folder name.
displayNamenameShown in the dashboard.
author, version, descriptionNoneShown on the Modules page.
iconpuzzleA Lucide icon name.
requires[]Modules that must load first.
dependencies.node[]npm packages Kora installs before loading the module.

Hooks

Add any of these methods to your module class.

MethodCalled
onStartup()After the module loads.
onShutdown()Before it unloads.
permissions()When Kora lists permissions. Return [{ id, label }]. They show up when editing roles.
overview(actor)When the Overview page loads. Return { stats, charts, lists }.
search(query, actor)When someone uses the dashboard search. Return [{ type, label, hint, href }].
fields(module, page)When another module's page is described. Return extra form fields for it.
decorate(module, page, rows)When another module's page lists rows. Return the rows with your data added.

overview and search only run for staff with the <module>.view permission.

Each entry in stats is { key, label, value, hint, icon, tone, period }. Set period to a number of days to show it as a badge in the card's corner instead of writing it into the label.

BuiltByBit uses fields and decorate to add a BuiltByBit resource picker to Core's product form:

fields(module, page) {
    if (module !== "core" || page !== "products") return [];
    return [{ key: "builtbybit_resource", label: "BuiltByBit resource", type: "reference", source: { module: "builtbybit", endpoint: "/resources", label: "title", value: "id" } }];
}

The kora Object

Every class gets the running instance as this.kora.

PropertyWhat it is
this.kora.databaseThe Sequelize connection.
this.kora.settingsValues from the Settings page.
this.kora.consoleThe logger.
this.kora.auditThe audit log.
this.kora.signerSigns and checks payloads with the response signing key.
this.kora.eventsThe event bus. See Events.
this.kora.modulesEvery loaded module and its parts.

Reaching Other Modules

this.kora.modules.models.get("core", "licenses");
this.kora.modules.handlers.get("core", "licenses");
this.kora.modules.config.values("core", "licenses");
this.kora.modules.lang.phrase("core", "messages", "valid");