{"id":22283105,"url":"https://github.com/lchrennew/koa-es-template","last_synced_at":"2026-05-18T09:11:03.665Z","repository":{"id":45567117,"uuid":"436138258","full_name":"lchrennew/koa-es-template","owner":"lchrennew","description":"node.js es koa服务基础模版","archived":false,"fork":false,"pushed_at":"2024-06-17T07:25:51.000Z","size":71,"stargazers_count":0,"open_issues_count":0,"forks_count":1,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-07-27T19:50:29.010Z","etag":null,"topics":["es6","koa"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/lchrennew.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-12-08T06:17:30.000Z","updated_at":"2024-06-17T07:25:54.000Z","dependencies_parsed_at":"2024-05-21T07:43:04.961Z","dependency_job_id":null,"html_url":"https://github.com/lchrennew/koa-es-template","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/lchrennew/koa-es-template","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lchrennew%2Fkoa-es-template","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lchrennew%2Fkoa-es-template/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lchrennew%2Fkoa-es-template/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lchrennew%2Fkoa-es-template/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/lchrennew","download_url":"https://codeload.github.com/lchrennew/koa-es-template/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lchrennew%2Fkoa-es-template/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":279002069,"owners_count":26083285,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-09T02:00:07.460Z","response_time":59,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["es6","koa"],"created_at":"2024-12-03T16:38:39.461Z","updated_at":"2025-10-09T21:08:09.247Z","avatar_url":"https://github.com/lchrennew.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# koa-es-template\n\nnode.js es koa服务基础模版\n\n**本模板需要node.js 17.2.0**\n\n## 依赖安装：\n\n### yarn\n\n```shell\nyarn\n```\n\n### npm\n\n```shell\nnpm i\n```\n\n## 开发指南\n\n### Controller基础教程\n\n#### 第1步：写一个新的Controller文件，比如`my-controller.js`\n\n```ecmascript 6\nimport Controller from \"./Controller.js\";\n\nexport default class MyControlller extends Controller {\n    constructor(config) {\n        super(config);\n    }\n}\n```\n\n#### 第2步：将新的Controller文件注册到上一级Controller里\n\n以`src/web/routes/index.js`为例：\n\n```ecmascript 6\nimport MyController from './my-controller.js'\n\nexport default class IndexController extends Controller {\n    constructor(config) {\n        super(config);\n        this.use('/my', new MyController(config)) // IndexController的基地址是/，所以MyController中所有接口的基地址都是 /my/\n    }\n}\n```\n\n#### 第3步：在新的Controller里添加请求响应逻辑\n\n```ecmascript 6\nimport Controller from \"./Controller.js\";\n\nexport default class MyControlller extends Controller {\n    constructor(config) {\n        super(config);\n        this.get('/users', this.getUsers)\n        this.post('/users', this.addUser)\n        this.delete('/users/:id', this.deleteUser)\n        this.put('/users/:id', this.replaceUser)\n        this.patch('/users/:id', this.updateUser)\n    }\n\n    // 对应的请求：GET /admin-api/my/users\n    async getUsers(ctx) {\n        ctx.body = []\n    }\n\n    // 对应的请求：POST /my/users\n    async addUser(ctx) {\n        const data = ctx.request.body\n        // 处理数据\n        return data\n    }\n\n    // 对应的请求，例如要删除ID=1的用户：DELETE /my/users/1\n    async deleteUser(ctx) {\n        const { id } = ctx.params\n        // 处理数据\n        ctx.body = { ok: true }\n    }\n\n    // 对应的请求，例如要替换ID=1的用户：PUT /my/users/1\n    async replaceUser(ctx) {\n        const data = ctx.request.body\n        // 处理数据\n        ctx.body = { ok: true }\n    }\n\n    // 对应的请求，例如要更新ID=1的用户：PATCH /my/users/1\n    async updateUser(ctx) {\n        const data = ctx.request.body\n        // 处理数据\n        ctx.body = { ok: true }\n    }\n}\n```\n\n### Controller高级教程\n\n#### 高级用法1：使用洋葱式的响应逻辑\n\n```ecmascript 6\nexport default class MyControlller extends Controller {\n    constructor(config) {\n        super(config);\n        // 下面这个DELETE请求处理会按从左到右的顺序逐层调用，并按照从右到左的顺序逐层返回\n        // 各层之间可以使用ctx.state传递数据\n        this.delete('/users/:id', [ this.requireAuth, this.userExists, this.deleteUser ])\n    }\n\n    // 第二个参数next是个异步函数，用来调用下一层\n    async requireAuth(ctx, next) {\n        const token = ctx.get('token')\n        const user = await getUserByToken(token)\n        if (!user) ctx.throw(401, JSON.stringify({ ok: false, error: '没有登录' }))\n        ctx.state.currentUser = user // 把当前用户存储到这里，以便在后续任何层都可以直接获取\n        await next() // 调用next会调用下一层的this.userExists\n\n        // this.userExists执行完后才会运行到这里（注意：如果this.userExists中调用了ctx.throw就不会运行到这里）\n        console.log('requireAuth完成了next的调用')\n    }\n\n    // 第二个参数next是个异步函数，用来调用下一层\n    async userExists(ctx, next) {\n        const { id } = ctx.params\n        const user = await getUserById(id)\n        if (!user) ctx.throw(404, JSON.stringify({ ok: false, error: '要删除的用户不存在' }))\n        ctx.state.userToBeDeleted = user // 把要删除的用户存储到这里，以便在后续任何层都可以直接获取\n        await next() // 调用next会调用下一层的this.deleteUser\n\n        // this.deleteUser执行完后才会运行到这里（注意：如果this.deleteUser中调用了ctx.throw就不会运行到这里）\n        console.log('userExists完成了next的调用')\n    }\n\n    async deleteUser(ctx) {\n        const { currentUser, userToBeDeleted } = ctx.state // 这样就可以取出前面几层传入的数据了\n        await deleteUser(userToBeDeleted, currentUser)\n        ctx.body = { ok: true }\n    }\n}\n```\n\n#### 高阶用法2：跨Controller复用处理层\n\n可以将需要复用的层提取为单独的处理函数，存到单独的文件中，并通过import的方式进行复用。 以上例中的requireAuth为例，可以单独将requireAuth提取到require-auth.js文件中：\n\n```ecmascript 6\n// require-auth.js文件的内容\nexport default async (ctx, next) =\u003e {\n    const token = ctx.get('token')\n    const user = await getUserByToken(token)\n    if (!user) ctx.throw(401, JSON.stringify({ ok: false, error: '没有登录' }))\n    ctx.state.currentUser = user // 把当前用户存储到这里，以便在后续任何层都可以直接获取\n    await next() // 调用next会调用下一层的this.userExists\n\n    // this.userExists执行完后才会运行到这里（注意：如果this.userExists中调用了ctx.throw就不会运行到这里）\n    console.log('requireAuth完成了next的调用')\n}\n```\n\n然后，在MyController中加以复用：\n\n```ecmascript 6\nimport requireAuth from './require-auth.js'\n\nexport default class MyControlller extends Controller {\n    constructor(config) {\n        super(config);\n        // requireAuth 是从require-auth.js引入的，它也可以被引入到其他的Controller中进行复用\n        this.delete('/users/:id', [ requireAuth, this.userExists, this.deleteUser ])\n    }\n\n    async userExists(ctx, next) {\n        // ...\n    }\n\n    async deleteUser(ctx) {\n        // ...\n    }\n}\n```\n\n### 打日志教程\n\n#### 在Controller内部\n\n可以直接使用this.logger\n\n```ecmascript 6\nthis.logger.info('hello')\n```\n\n#### 在其他js文件中\n\n需要先获取制定名称的logger，然后再使用：\n\n```ecmascript 6\nconst logger = defaultLogProvider('require-auth.js')\nlogger.debug('hello')\n```\n\n### 调用第三方接口指南\n\n#### 基础用法\n\n##### 步骤1：初始化远程接口调用对象\n\n```ecmascript 6\nimport { getApi } from \"./fetch.js\";\n\nconst api = getApi(process.env.SOME_API_BASE_URL) // 比如：SOME_API_BASE_URL=http://example.com/api\n```\n\n##### 步骤2：编写简单的GET调用\n\n```ecmascript 6\nimport apiDef from \"./apiDef.js\";\nimport { getApi, json, POST } from \"./fetch.js\";\n\nconst api = getApi(process.env.SOME_API_BASE_URL) // 比如：SOME_API_BASE_URL=http://example.com/api\n\nconst getUserById = async id =\u003e {\n    const resposne = await api('users') // 默认使用GET，请求地址为：http://example.com/api/users\n    const data = await response.json() // 如果接口返回json，可以直接这样获取json对象\n    return data\n}\n```\n\n##### 步骤3：编写简单的POST调用并发送json内容\n\n```ecmascript 6\nimport apiDef from \"./apiDef.js\";\nimport { getApi, json, POST } from \"./fetch.js\";\n\nconst api = getApi(process.env.SOME_API_BASE_URL) // 比如：SOME_API_BASE_URL=http://example.com/api\n\nconst updateUser = async user =\u003e {\n    // 下面的请求使用POST，请求发送的内容是user对象的json序列化串\n    const response = await api(`users/${user.id}`, POST, json(user)) // 还支持PUT、PATCH、DELETE\n    // ...\n}\n```\n\n#### 进阶用法\n\n##### 高阶用法1：扩展请求逻辑并用于特定的请求\n比如发送自定义请求头（如Authorization），遵循下面两个步骤即可\n\n**首先，要编写一个请求处理逻辑**\n\n```ecmascript 6\n// 这个逻辑一定要包含两个参数\n// 第一个参数是当前请求的上下文信息\n// 第二个参数是固定的\nconst auth = async (ctx, next) =\u003e {\n    ctx.header('Authorization', `token ${getToken()}`);\n    return next() // 函数一定要返回next()，注意，不要忘记return关键字\n}\n```\n\n**然后，直接将这个处理逻辑添加到需要它的请求调用参数中即可，例如：**\n```ecmascript 6\nconst updateUser = async user =\u003e {\n    // 下面的请求将会自动添加Authorization头\n    // 直接将auth添加到参数列表中，除url位置必须固定在第一个外，其他参数没有固定顺序\n    const response = await api(`users/${user.id}`, auth, PATCH, json(user)) \n}\n```\n\n##### 高阶用法2：将扩展逻辑用于所有请求\n通过改造api对象的逻辑实现，例如：\n\n```ecmascript 6\nimport apiDef from \"./apiDef.js\";\nimport { getApi, json, PATCH } from \"./fetch.js\";\n\nconst apiCore = getApi(process.env.SOME_API_BASE_URL)\n// 将auth逻辑直接插入到api的声明中，这样api发出的所有请求，都会经过auth的处理\nconst api = async (path, ...args) =\u003e await apiCore(path, auth, ...args)\n\nconst updateUser = async user =\u003e {\n    // 下面的请求将会自动添加Authorization头\n    const response = await api(`users/${user.id}`, PATCH, json(user))\n    // ...\n}\n```\n\n##### 高阶用法3：统一处理响应\n依然通过改造api对象来实现，例如，下面的代码实现了api数据的提取和错误的判断：\n```ecmascript 6\nconst apiCore = getApi(process.env.SOME_API_BASE_URL)\nconst api = async (...args)=\u003e {\n    const response = await apiCore(...args) // 假设响应的内容：{ok: true, data: {id: 1, name: '张三'}} 或者 {ok: false, error: '用户不存在'}\n    const {ok, data, error} = await response.json() // 将响应的内容转换为json对象，并直接解构到 ok、data、error这三个变量中\n    if(ok) return data // data的值：{id: 1, name: '张三'}\n    throw error // error: '用户不存在'\n}\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flchrennew%2Fkoa-es-template","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flchrennew%2Fkoa-es-template","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flchrennew%2Fkoa-es-template/lists"}