# koa-api-plus

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

Latest version **1.2.1** (published 2026-04-07) · ISC license · 0 weekly downloads

## Install

```sh
npm install koa-api-plus
pnpm add koa-api-plus
yarn add koa-api-plus
bun add koa-api-plus
```

## Health

**Score 60/100 (C)** — status: stable.

Positive: has types; esm support; no vulnerabilities; high quality score.

Warnings: low downloads.

## Facts

| | |
|---|---|
| Version | 1.2.1 |
| Published | 2026-04-07 |
| First published | 2023-06-03 |
| Weekly downloads | 0 |
| License | ISC |
| TypeScript types | bundled |
| Module format | ESM + CommonJS |
| Dependencies | 11 |
| Unpacked size | 394.3 KB |
| Known vulnerabilities | 0 |
| Install scripts | no |
| GitHub stars | 13 |
| Author | xygengcn |
| Maintainers | xygengcn |
| Keywords | koa-api, koa, api, RESTful, typescript |

## Links

- npm: https://www.npmjs.com/package/koa-api-plus
- Repository: https://github.com/xygengcn/koa-api
- Homepage: https://github.com/xygengcn/koa-api#readme
- Issues: https://github.com/xygengcn/koa-api/issues
- npm.io page: https://npm.io/package/koa-api-plus

## Dependencies (11)

- [koa](https://npm.io/package/koa.md) ^3.1.1
- [destroy](https://npm.io/package/destroy.md) ^1.2.0
- [koa-body](https://npm.io/package/koa-body.md) ^7.0.1
- [inversify](https://npm.io/package/inversify.md) 6.0.1
- [klaw-sync](https://npm.io/package/klaw-sync.md) ^6.0.0
- [@koa/router](https://npm.io/package/@koa/router.md) ^15.2.0
- [koa-compose](https://npm.io/package/koa-compose.md) ^4.1.0
- [on-finished](https://npm.io/package/on-finished.md) ^2.4.1
- [is-directory](https://npm.io/package/is-directory.md) 0.3.1
- [serialize-error](https://npm.io/package/serialize-error.md) 8.1.0
- [reflect-metadata](https://npm.io/package/reflect-metadata.md) ^0.1.13

## Alternatives

- [@openai/codex-sdk](https://npm.io/package/@openai/codex-sdk.md) — 731.4K weekly downloads
- [babel-plugin-transform-react-jsx](https://npm.io/package/babel-plugin-transform-react-jsx.md) — 565.0K weekly downloads
- [babel-helper-remove-or-void](https://npm.io/package/babel-helper-remove-or-void.md) — 508.5K weekly downloads
- [@pnpm/store-controller-types](https://npm.io/package/@pnpm/store-controller-types.md) — 186.9K weekly downloads
- [react-native-signature-canvas](https://npm.io/package/react-native-signature-canvas.md) — 155.6K weekly downloads

## Recent versions

- 1.2.1 (latest) — 2026-04-07
- 1.2.0 — 2026-03-24
- 1.1.0 — 2026-01-04
- 1.0.12 — 2024-10-30
- 1.0.11 — 2024-10-29
- 1.0.10 — 2023-10-12
- 1.0.9 — 2023-09-21
- 1.0.8 — 2023-09-11
- 1.0.7 — 2023-09-11
- 1.0.6 — 2023-08-12
- 1.0.5 — 2023-06-24
- 1.0.4 — 2023-06-24
- 1.0.3 — 2023-06-24
- 1.0.2 — 2023-06-22
- 1.0.1 — 2023-06-03

## README

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

## 测试网站

[测试网站 api.xygeng.cn](https://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 风格）

```ts
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 文档

```bash
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 调用）

```ts
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 路由

```ts
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 网关执行真实接口：

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

`createMcpRoutes` 默认就是同端口执行（same-origin），无需额外字段。

### 一键启用 MCP（零样板代码）

```ts
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：

```ts
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/tools` 的 `meta.conflicts` 返回冲突明细。

### OpenCode 接入 koa-api MCP 教程

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

1. 启动 API 并开启 MCP：

```ts
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();
```

2. 先本地验证 MCP 路由：

```bash
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"}}'
```

3. 在 OpenCode 中新增 MCP 服务：

-   服务地址填写：`http://127.0.0.1:3000/mcp`
-   OpenCode 会通过 `/mcp/tools` 自动发现可调用工具
-   AI 调用工具时，OpenCode 会请求 `/mcp/invoke`

4. 在 OpenCode 对话里直接调用工具：

```text
请调用 get_user_profile，参数 id=1001，并总结结果
```

5. 生产环境建议：

-   给 `/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()` 优先，自动推断只补充缺失项。

### 使用方法

```ts
// 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` / `currentContext` helper

### 基础路由用法

```ts
//
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

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

export function buildAuditLog(action: string) {
    return {
        action,
        requestId: getRequestId()
    };
}
```

```ts
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`

### 实例函数

```js
// 事件监听
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 消费和打包结果

---
_Source: https://npm.io/package/koa-api-plus · Machine-readable twin of the npm.io package page. Health data is recomputed on every publish._
