npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

koa-api-plus

v1.2.1

Published

基于Typescript + KOA 开发的 Restful APi 接口

Readme

基于 Typescript + KOA 开发的 Restful APi 接口

测试网站

测试网站 api.xygeng.cn

特点


  • ts 开发
  • 支持装饰器和 控制器 创建接口
  • 具有日志记录功能

安装


快速开始

  • 安装依赖
pnpm install
  • 运行测试
pnpm test
  • 打包命令
pnpm build
  • 测试命令
pnpm test
  • 工具链
包管理器使用 pnpm,开发/构建统一由 Vite 管理,测试基于 Vitest
  • 生产环境
pnpm add koa-api-plus
  • 验证产物可消费
pnpm test:dist
  • 发布前检查
pnpm release:check
  • 贡献指南
CONTRIBUTING.md
  • 变更记录
CHANGELOG.md

控制器(重点)


存放控制器的目录,防止路由重复,文件名将默认为路由前缀。路由规则:/文件夹名/类装饰器(文件名,index文件为斜杠)/方法装饰器(方法名),支持不断嵌套

控制器文件命名规则:index.controller.ts

装饰器

  • Controller()装饰器,在类前面使用:@Controller(路由前缀),默认文件名就是前缀

    @Controller('user')

  • Get/Post,请求方法,在类方法前面使用:@函数(路由)

  • Put/Patch/Delete 同样支持,对应 HTTP 方法装饰器为 @Put() / @Patch() / @Delete()

    @Get('/login') 对应的路由就是 /user/login

  • CurrentContext() 属性装饰器,可直接在类里读取 Koa3 currentContext

    @CurrentContext() private readonly currentContext!: Context

  • 默认会为每个请求生成 requestId,并写入响应体和响应头 x-request-id

  • 可直接从包中导出 helper:getCurrentContext()getRequestId(),第三方业务工具函数也能读取当前请求上下文

  • 文档装饰器:@Request()@Response()@Mcp()

  • @Controller({ path, doc }) 支持一次性定义路由前缀 + 控制器级文档信息(推荐)

  • 也支持 @Controller(path, { doc }) 写法

API 文档装饰器(Swagger 风格)

import { Controller, Get, Mcp, Param, Request, Response } from 'koa-api-plus';

@Controller({ path: '/user', doc: { tag: 'User', summary: 'User APIs' } })
export default class UserController {
    @Request({ in: 'query', name: 'id', type: 'string', required: true, description: 'User id' })
    @Response({ status: 200, description: 'Success' })
    @Mcp({ name: 'get_user_profile', description: 'Get user profile MCP tool' })
    @Get('/profile', { summary: 'Get user profile', description: 'Fetch profile by id', tags: ['User'] })
    public profile(@Param.Query('id') id: string) {
        return { id, name: 'demo' };
    }
}

控制器级文档统一由 @Controller({ path, doc })@Controller(path, { doc }) 提供。

一键生成 API Markdown 文档

pnpm doc:gen --baseUrl ./src/controllers --title "My API" --version 1.0.0 --output docs/api.md

生成物:

  • docs/api.md:接口文档(Markdown)
  • docs/api.mcp-tools.json:MCP 工具清单(AI 接入可直接消费)
  • docs/api.openapi.json:OpenAPI 3.0 JSON(Swagger 生态可直接使用)

MCP 接入(AI 调用)

import { createApiMcpInvoker, getApiMcpTools, getApiSpec } from 'koa-api-plus';

const spec = getApiSpec({ title: 'My API', version: '1.0.0' });
const tools = getApiMcpTools(spec);
const invoker = createApiMcpInvoker(tools, { baseUrl: 'http://127.0.0.1:3000' });

const result = await invoker.invoke('get_user_profile', { id: '1001' });

内置 MCP 路由

import Api, { createMcpRoutes, getApiMcpTools, getApiSpec } from 'koa-api-plus';

const api = new Api({ baseUrl: './src/controllers' });
api.callback();

const spec = getApiSpec({ title: 'My API', version: '1.0.0' });
const tools = getApiMcpTools(spec);

api.use(createMcpRoutes(tools, { enabled: true, basePath: '/mcp' }));

// GET  /mcp/tools
// POST /mcp/invoke

如果希望 MCP 网关执行真实接口:

api.use(createMcpRoutes(tools, { enabled: true, basePath: '/mcp' }));

createMcpRoutes 默认就是同端口执行(same-origin),无需额外字段。

一键启用 MCP(零样板代码)

import Api from 'koa-api-plus';

const api = new Api({
    baseUrl: './src/controllers',
    mcp: {
        enabled: true,
        basePath: '/mcp',
        docs: {
            title: 'My API',
            version: '1.0.0'
        }
    }
});

api.start();

开启后默认提供:

  • GET /mcp/tools:返回文档装饰器生成的工具清单
  • POST /mcp/invoke:按工具名在同端口执行目标 API

MCP 聚合(本机 + 远端统一路由)

可以在同一个 /mcp/* 下聚合远端 MCP:

const api = new Api({
    baseUrl: './src/controllers',
    mcp: {
        enabled: true,
        basePath: '/mcp',
        remotes: [
            { name: 'crm', baseUrl: 'https://crm.example.com/mcp' },
            { name: 'pay', baseUrl: 'https://pay.example.com/mcp', headers: { authorization: 'Bearer <token>' } }
        ],
        cacheMs: 30000,
        docs: { title: 'My API', version: '1.0.0' }
    }
});

行为:

  • GET /mcp/tools:返回本机工具 + 远端工具合并列表
  • 远端工具自动加前缀:<remoteName>.<toolName>(例如 crm.get_customer
  • POST /mcp/invoke:本机工具走同端口调用;带前缀的工具自动转发到对应远端 MCP
  • GET /mcp/health:查看远端 MCP 健康状态(支持 ?refresh=true 强制刷新)

冲突处理:

  • 若工具名冲突,默认保留首次出现的工具,并在 /mcp/toolsmeta.conflicts 返回冲突明细。

OpenCode 接入 koa-api MCP 教程

下面给一套最短闭环流程,方便在 OpenCode 中让 AI 直接调用你的 koa-api 接口能力。

  1. 启动 API 并开启 MCP:
import Api from 'koa-api-plus';

const api = new Api({
    baseUrl: './src/controllers',
    port: 3000,
    mcp: {
        enabled: true,
        basePath: '/mcp',
        docs: {
            title: 'My API',
            version: '1.0.0'
        }
    }
});

api.start();
  1. 先本地验证 MCP 路由:
curl http://127.0.0.1:3000/mcp/tools
curl -X POST http://127.0.0.1:3000/mcp/invoke \
  -H 'content-type: application/json' \
  -d '{"name":"get_user_profile","input":{"id":"1001"}}'
  1. 在 OpenCode 中新增 MCP 服务:
  • 服务地址填写:http://127.0.0.1:3000/mcp
  • OpenCode 会通过 /mcp/tools 自动发现可调用工具
  • AI 调用工具时,OpenCode 会请求 /mcp/invoke
  1. 在 OpenCode 对话里直接调用工具:
请调用 get_user_profile,参数 id=1001,并总结结果
  1. 生产环境建议:
  • /mcp/* 增加鉴权(例如 token/header)
  • @Mcp() 中补全 descriptioninputSchema,提高 AI 调用稳定性
  • 每次接口变更执行 pnpm doc:gen,保持文档、工具、接口一致

说明:不同 OpenCode 版本界面入口可能略有区别,但接入要点一致:指向同一 basePath(例如 /mcp)即可。

完整可运行 demo 见:docs/opencode-mcp-demo.md(包含启动、验证、OpenCode 接入全流程)。

自动参数推断

  • 如果没有写 @Request(),框架会根据 @Param.Query() / @Param.Body() 自动推断基础文档参数。
  • 显式 @Request() 优先,自动推断只补充缺失项。

使用方法

// index.js
import { Controller, Get, Post } from 'koa-api-plus';
@Controller()
export default class IndexController {
    @Get('/test')
    async hello() {
        return {
            title: 'haha'
        };
    }
}
// [Get] /test => {}

// user/index.controller.ts
import { Controller, Get, Post } from 'koa-api-plus';
@Controller()
export default class IndexController {
    @Post('/test')
    async hello() {
        return {
            title: 'haha'
        };
    }
}

// 【Post】 /user/test =>{}

测试用例

迁移后,原有示例已全部改为 Vitest 集成测试,推荐直接阅读 tests/

  • tests/controllers/basic-routes.test.ts:基础 GET/POST/PUT/DELETE 行为
  • tests/controllers/error-handling.test.ts:同步/异步错误响应
  • tests/controllers/middleware-behavior.test.ts:路由中间件与控制器中间件
  • tests/controllers/static-assets.test.ts:静态资源与 API 共存
  • tests/controllers/image-response.test.ts:图片流响应
  • tests/controllers/upload.test.ts:文件上传
  • tests/controllers/sse.test.ts:SSE 输出
  • tests/helpers/request-context.test.tsrequestId / currentContext helper

基础路由用法

//
import Api, { ApiLogger, Context, Controller, CurrentContext, Delete, Get, Logger, Param, Post, Put } from '../../src';

type User = {
    id: number;
    name: {
        firstName: string;
        lastname: string;
    };
};

@Controller()
export class GetController {
    @CurrentContext()
    private readonly currentContext!: Context;

    @Get('/get')
    // 获取query参数
    public get(@Param.Query<User>('id') id: number) {
        if (id) {
            return {
                id,
                requestId: this.currentContext.state?.requestId || null
            };
        }
        return null;
    }

    @Put('/put')
    public put(@Param.Query<User>('id') id: number) {
        return { id, method: 'put' };
    }

    @Delete('/delete')
    public delete(@Param.Query<User>('id') id: number) {
        return { id, method: 'delete' };
    }
}

@Controller('/post')
export class PostController {
    // 定义日志
    @Logger()
    private logger!: ApiLogger;
    @Post('/')
    // 获取body参数
    public post(@Param.Body<User>('id') id: number, @Param.Body<User>('name.firstName') firstName: string) {
        this.logger.log('post测试:', { id, firstName });
        if (id && firstName) {
            return {
                id,
                firstName
            };
        }
        return null;
    }
}

// 创建实例
const api = new Api();

api.on('log', (...args) => {
    console.log('[log]', ...args);
});
api.on('start', () => {
    console.log('[start]');
});

api.on('error', (e) => {
    console.error('[error]', e);
});

// 启动
api.start();

在 utils 工具函数里获取 requestId

import { getRequestId } from 'koa-api-plus';

export function buildAuditLog(action: string) {
    return {
        action,
        requestId: getRequestId()
    };
}
import { Controller, Get } from 'koa-api-plus';
import { buildAuditLog } from './utils/audit';

@Controller('/audit')
export default class AuditController {
    @Get('/detail')
    public detail() {
        return buildAuditLog('detail');
    }
}
  • 这个 helper 在请求链路内调用时可返回当前请求的 requestId
  • 如果在启动脚本、定时任务、独立命令中调用,没有请求上下文时会返回 null

实例函数

// 事件监听
app.on();
// koa默认中间件
app.use();

// 默认启动服务
app.start();

打包流程

1、用 pnpm test 验证所有框架能力

2、生产构建通过 vite build 输出 dist/index.cjsdist/index.mjsdist/index.d.ts

3、控制器自动加载已改为基于动态 import(),更适配 Vite/ESM 环境

4、发布前可运行 pnpm release:check,依次验证测试、构建、dist 消费和打包结果