axiosleo/node-koapp/koapp-controller

Claude Code and agent skill by axiosleo/node-koapp

Trust tier 3
  • community
Source check pending

Checking that the SKILL.md file is still in the repository.

Organize route handlers into reusable classes by extending the @axiosleo/koapp Controller base class. Use when grouping related handlers, reusing response helpers (this.success/failed/result/response/error/log), sharing state across handlers, or scaffolding service modules for a koapp project.

Install

npx vskill@latest install axiosleo/node-koapp/koapp-controller

SKILL.md

@axiosleo/koapp Controller

Source: `src/controller.js`.

Controller is a thin base class that mirrors the response helpers as instance methods. Extend it to keep related handlers together and share auxiliary state.

Import

javascript
const { Controller } = require('@axiosleo/koapp');

Instance API

MethodEquivalent functionNotes
this.success(data?, headers?)success(data, headers)200 OK JSON envelope
this.failed(data?, code?, status?, headers?)failed(data, code, status, headers)Non-2xx JSON envelope
this.result(data, status?, headers?)result(data, status, headers)Raw body pass-through
this.response(data, code, status?, headers?)response(data, code, status, headers)Full control envelope
this.error(status, msg, headers?)error(status, msg, headers)Shortcut error envelope
this.log(...data)debug.log from @axiosleo/cli-toolConditional logging

All this.success / failed / result / response / error throw, same as the functional helpers. See koapp-response for envelope details.

Basic usage

javascript
const { Controller, Router } = require('@axiosleo/koapp');
class HealthController extends Controller {
async ping(context) {
this.log('ping from', context.request_id);
this.success({ pong: true });
}
async version() {
this.success({ version: require('../package.json').version });
}
}
const health = new HealthController();
const router = new Router('/health');
router.get('/ping', (ctx) => health.ping(ctx));
router.get('/version', () => health.version());

Why use Controller over plain functions?

  • Shared state: load DB handles, caches, or config once in the

constructor and reuse across methods.

  • Inheritance: define a BaseController with common middlewares or

helpers, then extend it per module (the init scaffolding template at `assets/monorepo/apps/services/src/modules/controller.ts.tmpl` does this).

  • Testability: easier to mock a controller instance than free-floating

functions.

Binding handlers to routes

Controller methods expect a context argument and may use this, so bind them explicitly when registering with the router:

javascript
router.get('/users/{:id}', (ctx) => controller.find(ctx));
router.post('/users', (ctx) => controller.create(ctx));
// or with arrow methods
class UsersController extends Controller {
find = async (ctx) => {
const id = Number(ctx.params.id);
this.success({ id });
};
}

Pure wrappers (ctx) => controller.method(ctx) preserve this and avoid .bind clutter.

Error handling

Since this.success / failed / ... throw, any code after them does not run. The workflow catches the throw and writes the response. Keep try / catch only where you intend to shape a different error:

javascript
async create(context) {
try {
const user = await this.db.users.insert(context.body);
this.success({ id: user.id });
} catch (err) {
if (err.code === 'DUP_ENTRY') {
this.failed({ email: context.body.email }, '409;Email Already Registered', 409);
}
throw err; // let the workflow convert to 500
}
}

Logging

this.log is wired to debug.log from @axiosleo/cli-tool. Enable it by running with DEBUG=1 (or the cli-tool equivalent), or by setting debug: true on the application.

javascript
this.log('loading user', context.params.id);

Composing with validators

Validation stays on the router:

javascript
router.push('post', '/users', (ctx) => controller.create(ctx), {
body: {
rules: {
name: 'required|string',
email: 'required|email'
}
}
});

The validator runs before the handler, so by the time controller.create executes you already have a validated context.body.

Scaffold output

There is no code generator; modules are written by hand following the layout produced by npx @axiosleo/koapp init <name> (monorepo template). It ships a project-local BaseController at apps/services/src/modules/controller.ts that extends Controller and adds request-scoped helpers (the template source is `assets/monorepo/apps/services/src/modules/controller.ts.tmpl`):

typescript
import type { KoaContext } from '@axiosleo/koapp';
import { Controller } from '@axiosleo/koapp';
export class BaseController extends Controller {
/** Resolve the app scope for the current request (Api-Key vs admin session). */
protected appId(context: KoaContext): string | null { /* ... */ }
protected isAdmin(context: KoaContext): boolean {
return context.auth?.is_admin === true;
}
}

Each business module then follows the three-file convention src/modules/<name>/<name>.router.ts + <name>.controller.ts + <name>.model.ts, where the controller extends BaseController and is exported as a singleton bound to routes with arrow wrappers:

typescript
// src/modules/app/app.controller.ts
import { BaseController } from '../controller';
export class AppController extends BaseController {
async list(context: KoaContext) {
this.success({ list: [] });
}
}
export default new AppController();
// src/modules/app/app.router.ts
import controller from './app.controller';
const router = new Router('/apps');
router.get('', (ctx) => controller.list(ctx));

Common pitfalls

  • Forgetting to pass context into instance methods: router.get('/x', controller.handler) loses this when the router calls the method as a free function. Use arrow wrappers.
  • Returning this.success(...) from a handler is redundant - the throw short-circuits execution.
  • Controllers are long-lived across requests; never stash per-request data on this. Use context or local variables instead.

See also

  • Response helper details: koapp-response
  • Grouping controllers under a router tree: koapp-router
  • Concrete controller recipes: examples.md

Security

  • Security scanPassed, 95 of 100Pattern checks on the skill's files.
  • Model reviewPassed

Quality evaluation

  • Task evaluationNot runNo evaluation yet. Run one in Skill Studio.

Source

Popularity

Repository stars
44
Repository forks
11
Repository npm downloads, 7 days
548
7-day trend score
34

Stars and forks are for the whole GitHub repository, not this skill alone. Downloads are for the repository's npm package. Refreshed 1 Aug 2026, 3 months ago.

Works with

Any agent vskill installs to: 56 targets in all. These 39 have a page on this site.

  • Claude Code
  • Cursor
  • GitHub Copilot
  • Windsurf
  • Codex
  • Gemini CLI
  • Cline
  • Amp
  • Roo Code
  • Goose
  • Continue
  • Kilo Code
  • Junie
  • Trae
  • OpenHands
  • Qwen Code
  • AI Agent
  • Tabnine Chat
  • Sourcegraph Cody
  • Amazon Q
  • Firebase Genkit
  • Sweep
  • Devin
  • Phind
  • Mentat
  • Claude Desktop
  • Aider
  • GPT Pilot
  • Plandex
  • bolt.new
  • v0
  • Replit Agent
  • Anse
  • TypingMind
  • BetterChatGPT
  • LibreChat
  • Warp AI
  • Pieces
  • Fig AI
Embed badge
vskill VERIFIED badge
[![vskill VERIFIED](https://verified-skill.com/api/v1/skills/axiosleo/node-koapp/koapp-controller/badge)](https://verified-skill.com/skills/axiosleo/node-koapp/koapp-controller)

Something wrong with this skill or its page? Tell us.