{"id":18910051,"url":"https://github.com/cheton/koa-example","last_synced_at":"2025-08-13T18:09:09.386Z","repository":{"id":24448010,"uuid":"27850274","full_name":"cheton/koa-example","owner":"cheton","description":"koa-example","archived":false,"fork":false,"pushed_at":"2014-12-11T02:32:45.000Z","size":136,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-07-30T12:58:09.347Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/cheton.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2014-12-11T02:27:35.000Z","updated_at":"2019-06-25T09:32:33.000Z","dependencies_parsed_at":"2022-07-19T23:17:50.354Z","dependency_job_id":null,"html_url":"https://github.com/cheton/koa-example","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/cheton/koa-example","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cheton%2Fkoa-example","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cheton%2Fkoa-example/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cheton%2Fkoa-example/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cheton%2Fkoa-example/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/cheton","download_url":"https://codeload.github.com/cheton/koa-example/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cheton%2Fkoa-example/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":269411520,"owners_count":24412428,"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-08-08T02:00:09.200Z","response_time":72,"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":[],"created_at":"2024-11-08T09:39:10.103Z","updated_at":"2025-08-13T18:09:09.332Z","avatar_url":"https://github.com/cheton.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"![](https://raw.githubusercontent.com/cheton/koa-example/master/koa.png)\n\nExpressive middleware for node.js using generators via [co](https://github.com/visionmedia/co) to make web applications and APIs more enjoyable to write. Koa's middleware flow in a stack-like manner allowing you to perform actions downstream, then filter and manipulate the response upstream. Koa's use of generators also greatly increases the readability and robustness of your application.\n\nOnly methods that are common to nearly all HTTP servers are integrated directly into Koa's small ~550 SLOC codebase. This includes things like content-negotiation, normalization of node inconsistencies, redirection, and a few others.\n\nNo middleware are bundled with koa. If you prefer to only define a single dependency for common middleware, much like Connect, you may use [koa-common](https://github.com/koajs/common).\n\n## Installation\n\n    $ npm install koa\n\nTo use Koa you must be running node 0.11.9 or higher for generator support, and must run node(1) with the --harmony flag. If you don't like typing this, add an alias to your shell profile:\n\n    alias node='node --harmony'\n\n\n## Resources\n\n - Expressive middleware for node.js using generators \n    http://koajs.com\n    https://github.com/koajs/koa\n\n - What's is this things called Generators?\n    http://tobyho.com/2013/06/16/what-are-generators/\n\n - Next-gen Express: Building a simple webserver with Koa.js\n    http://www.olindata.com/blog/2014/06/next-gen-express-building-simple-webserver-koajs\n\n - Docs\n    https://github.com/koajs/koa/tree/master/docs\n\n - Docs \u003e Guide\n    https://github.com/koajs/koa/blob/master/docs/guide.md\n\n - Example Koa apps\n    https://github.com/koajs/examples\n\n## Generators\n\nnaturalNumbers.js\n```js\nfunction *naturalNumbers() {\n    var n = 1;\n    while (n \u003c 3) {\n        yield n++;\n    }\n}\n\n//for (var number of naturalNumbers()) {\n//    console.log('numbers is ', number);\n//}\n\nvar numbers = naturalNumbers();\nconsole.log(numbers.next()); // { value: 1, done: false }\nconsole.log(numbers.next()); // { value: 2, done: false }\nconsole.log(numbers.next()); // { value: undefined, done: true }\n```\n\nfibonacci.js\n```js\nfunction *fibonacci() {\n    var prev = 0, curr = 1, tmp;\n    while (true) {\n        tmp = prev;\n        prev = curr;\n        curr = tmp + curr;\n\n        yield curr;\n    }\n}\n\nvar seq = fibonacci(); // returns a iterator object\n\nconsole.log(seq.next().value); // returns 1\nconsole.log(seq.next().value); // returns 2\nconsole.log(seq.next().value); // returns 3\nconsole.log(seq.next().value); // returns 5\n```\n\nconsumer.js\n```js\nfunction *consumer() {\n    while (true) {\n        var val = yield null;\n        console.log('\\nGot value', val);\n    }\n}\n\nvar c = consumer();\nconsole.log(c.next(1));\n    // { value: null, done: false }\nconsole.log(c.next(2));\n    // Got value 2\n    // { value: null, done: false }\nconsole.log(c.next(3));\n    // Got value 3\n    // { value: null, done: false }\n```\n\n## Koa Examples\n\nkoa-basic.js\n```js\nvar koa = require('koa');\nvar app = koa();\n\napp.use(function *body() {\n    this.body = 'Hello FEDs';\n});\n\napp.listen(5000);\n```\n\nkoa-middleware.js\n```js\nvar koa = require('koa');\nvar app = koa();\nvar logger = require('koa-logger'); // https://github.com/koajs/logger\nvar serve = require('koa-static'); // https://github.com/koajs/static\n\n// logger middleware\napp.use(logger());\n\n// static middleware\napp.use(serve(__dirname + '/web'));\n\napp.use(function *body() {\n    this.body = 'Hello FEDs';\n});\n\nvar server = app.listen(5000, function() {\n    console.log('Listening on port %d', server.address().port);\n});\n```\n\nkoa-router.js\n```js\nvar koa = require('koa');\nvar router = require('koa-router'); // https://github.com/alexmingoia/koa-router\nvar app = koa();\n\napp.use(router(app)); // register method get\n\n// After the router has been initialized you can register routes:\napp.get('/example', function *example() {\n    this.body = 'Example route';\n});\n\napp.use(function *body() {\n    this.body = 'Hello FEDs';\n});\n\napp.listen(5000);\n```\n\nkoa-custom.js\n```js\n//\n// https://github.com/koajs/koa/blob/master/docs/guide.md\n//\nvar koa = require('koa');\nvar app = koa();\n\n/**\n * @description \n * @params next it's a handle to the subsequent middleware function\n *\n * app.use(function *(next) {\n *     yield next;\n * });\n */\n\n// responseTime: track how long it takes for a request to propagate through Koa by adding an 'X-Response-Time' header field\napp.use(function *responseTime(next) {\n    console.log('[1] responseTime: start');\n    var start = new Date;\n    yield next;\n    var ms = new Date - start;\n    this.set('X-Response-Time', ms + 'ms');\n    console.log('[1] responseTime: end');\n});\n\n// logger\napp.use(function *logger(next){\n    console.log('[2] logger: start');\n    var start = new Date;\n    yield next;\n    var used = new Date - start;\n    console.log('\u003e method=%s originalUrl=%s status=%s used=%sms', this.method, this.originalUrl, this.status, used);\n    console.log('[2] logger: end');\n});\n\n// contentLength\napp.use(function *contentLength(next){\n    console.log('[3] contentLength: start');\n    yield next;\n    if ( ! this.body) {\n        return;\n    }\n    this.set('Content-Length', this.body.length);\n    console.log('[3] contentLength: end');\n});\n\n// body\napp.use(function *body(next){\n    console.log('[4] body: start');\n    yield next;\n    if (this.path !== '/') {\n        return;\n    }\n    this.body = 'Hello FEDs';\n    console.log('[4] body: end');\n});\n\napp.listen(5000);\n```\n\n## The Promise\n\nThe following paragraph is originated from:\nhttp://tobyho.com/2013/06/16/what-are-generators/ \n\nIn Javascript, especially in Node, IO operations are generally done as asynchronous operations that require a callback. When you have to do multiple async operations one after another it looks like this:\n```js\nfs.readFile('blog_post_template.html', function(err, tpContent) {\n    fs.readFile('my_blog_post.md', function(err, mdContent) {\n        resp.end(template(tpContent, markdown(String(mdContent))));\n    });\n});\n```\n\nIt gets worse when you add in error handling:\n```js\nfs.readFile('blog_post_template.html', function(err, tpContent) {\n    if (err) {\n        resp.end(err.message);\n        return;\n    }\n    fs.readFile('my_blog_post.md', function(err, mdContent) {\n        if (err) {\n            resp.end(err.message);\n            return;\n        }\n        resp.end(template(tpContent, markdown(String(mdContent))));\n    });\n});\n```\n\nThe promise of generators is that you can now write the equivalent code in a straight-line fashion using generators:\n```js\ntry {\n    var tpContent = yield readFile('blog_post_template.html');\n    var mdContent = yield readFile('my_blog_post.md');\n    resp.end(template(tpContent, markdown(String(mdContent))));\n} catch(e){\n    resp.end(e.message);\n}\n```\n\nThis is fantastic! Aside from less code and asthetics, it also has these benefits:\n\nLine independence: the code for one operation is no longer tied to the ones that come after it. If you want to reorder of operations, simply switching the lines. If you want to remove an operation, simply deleting the line.\nSimpler and DRY error handling: where as the callback-based style required error handling to be done for each individual async operation, with the generator-based style you can put one try/catch block around all the operations to handle errors uniformly - generators gives us back the power of try/catch exception handling.\n\nThe first thing to realize is that the async operations need to take place outside of the generator function. This means that some sort of \"controller\" will need to handle the execution of the generator, fulfill async requests, and return the results back. So we'll need to pass the generator to this controller, for which we'll just make a run() function:\n```js\nrun(function*(){\n    try{\n        var tpContent = yield readFile('blog_post_template.html');\n        var mdContent = yield readFile('my_blog_post.md');\n        resp.end(template(tpContent, markdown(String(mdContent))));\n    } catch(e){\n        resp.end(e.message);\n    }\n});\n```\n\nrun() has the responsibility of calling the generator object repeatedly via next(), and fulfill a request each time a value is yielded. It will assume that the requests it receives are functions that take a single callback parameter which takes an err, and another value argument - conforming to the Node style callback convention. When err is present, it will call throw() on the generator object to propagate it back into the generator's code path. The code for run() looks like:\n```js\nfunction run(genfun) {\n    // instantiate the generator object\n    var gen = genfun();\n    // This is the async loop pattern\n    function next(err, answer) {\n        var res;\n        if (err) {\n            // if err, throw it into the wormhole\n            return gen.throw(err);\n        } else {\n            // if good value, send it\n            res = gen.next(answer);\n        }\n        if ( ! res.done) {\n            // if we are not at the end\n            // we have an async request to\n            // fulfill, we do this by calling \n            // `value` as a function\n            // and passing it a callback\n            // that receives err, answer\n            // for which we'll just use `next()`\n            res.value(next);\n        }\n    }\n    // Kick off the async loop\n    next();\n}\n```\n\nNow given that, readFile takes the file path as parameter and needs to return a function:\n```js\nfunction readFile(filepath) {\n    return function(callback) {\n        fs.readFile(filepath, callback);\n    }\n}\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcheton%2Fkoa-example","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcheton%2Fkoa-example","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcheton%2Fkoa-example/lists"}