Skip to content

Create Your First Otok Plugin

Create your first Otok plugin#

This guide walks through a minimal plugin that registers a server route.

1. Scaffold the package#

packages/my-otok-plugin/
  package.json
  src/index.ts

Use @kamod-ch/otok-plugin-hello as a reference implementation.

2. Define the plugin#

import { definePlugin } from "@kamod-ch/otok";

export interface MyPluginOptions {
  greeting?: string;
}

export default definePlugin<MyPluginOptions>({
  name: "my-otok-plugin",
  version: "0.1.0",
  schema: {
    parse(input) {
      if (input !== undefined && typeof input !== "object") {
        throw new Error("options must be an object");
      }
      return input ?? {};
    },
  },
  config(options) {
    return {
      env: {
        MY_GREETING: options?.greeting ?? "hello",
      },
    };
  },
  configureApp({ app }) {
    app.get("/api/my-plugin", (c) => c.json({ ok: true }));
  },
});

3. Enable it in the app#

// otok.config.ts
import { defineConfig } from "@kamod-ch/otok";
import myPlugin from "my-otok-plugin";

export default defineConfig({
  plugins: [myPlugin({ greeting: "hi" })],
});

4. Apply plugins in the server entry#

import { loadOtokResolvedConfig } from "virtual:otok-config";

const { runtime, applyAppPlugins } = await loadOtokResolvedConfig();

const app = createOtokApp({
  routes,
  ...runtime,
  configure: (app) => {
    void applyAppPlugins(app);
  },
});

5. Test the plugin#

Use @kamod-ch/otok-test with resolveOtokConfig():

import { createTestApp } from "@kamod-ch/otok-test";
import { resolveOtokConfig } from "@kamod-ch/otok";
import myPlugin from "my-otok-plugin";

const resolved = await resolveOtokConfig({ plugins: [myPlugin()] }, { root: "/tmp", mode: "test", command: "build" });

const app = createTestApp({
  routes: [{ path: "/" }],
  configure: (app) => {
    void resolved.applyAppPlugins(app);
  },
});

const response = await app.request("/api/my-plugin");

Server-only code#

Keep Node-only imports in server hooks such as configureApp, configureServer, and buildStart. Do not import them from client-facing virtual modules unless the module is server-only.

Next steps#

  • Add virtual modules under virtual:otok-plugin/<plugin-name>/<id>
  • Extend typed env via envSchema
  • Contribute official extensions as separate packages (@kamod-ch/otok-*, @kamod-ch/otok-*)