koa-api-plus
v1.2.1
Published
基于Typescript + KOA 开发的 Restful APi 接口
Maintainers
Readme
基于 Typescript + KOA 开发的 Restful APi 接口
测试网站
特点
- 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:本机工具走同端口调用;带前缀的工具自动转发到对应远端 MCPGET /mcp/health:查看远端 MCP 健康状态(支持?refresh=true强制刷新)
冲突处理:
- 若工具名冲突,默认保留首次出现的工具,并在
/mcp/tools的meta.conflicts返回冲突明细。
OpenCode 接入 koa-api MCP 教程
下面给一套最短闭环流程,方便在 OpenCode 中让 AI 直接调用你的 koa-api 接口能力。
- 启动 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();- 先本地验证 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"}}'- 在 OpenCode 中新增 MCP 服务:
- 服务地址填写:
http://127.0.0.1:3000/mcp - OpenCode 会通过
/mcp/tools自动发现可调用工具 - AI 调用工具时,OpenCode 会请求
/mcp/invoke
- 在 OpenCode 对话里直接调用工具:
请调用 get_user_profile,参数 id=1001,并总结结果- 生产环境建议:
- 给
/mcp/*增加鉴权(例如 token/header) - 在
@Mcp()中补全description和inputSchema,提高 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.ts:requestId/currentContexthelper
基础路由用法
//
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.cjs、dist/index.mjs、dist/index.d.ts
3、控制器自动加载已改为基于动态 import(),更适配 Vite/ESM 环境
4、发布前可运行 pnpm release:check,依次验证测试、构建、dist 消费和打包结果
