diff --git a/README.md b/README.md
index 9abd098..291da22 100644
--- a/README.md
+++ b/README.md
@@ -47,6 +47,7 @@ Quick Reference
## Nodejs
[Express.js](./docs/expressjs.md)
+[Koa.js](./docs/koajs.md)
## 工具
diff --git a/docs/koajs.md b/docs/koajs.md
new file mode 100644
index 0000000..799f9a6
--- /dev/null
+++ b/docs/koajs.md
@@ -0,0 +1,424 @@
+Koajs 备忘清单
+===
+
+基于 Node.js 平台的下一代 web 开发框架,包含 [Koa](https://koajs.com/) 的 API 参考列表和一些示例。
+
+
+入门
+---
+
+### Hello World
+
+
+[Koa](https://koajs.com/) 需要 [node v7.6.0](https://nodejs.org) 或更高版本来支持ES2015、异步方法,你可以安装自己支持的 `node` 版本
+
+- 安装依赖
+ ```bash
+ $ mkdir myapp # 创建目录
+ $ cd myapp # 进入目录
+ $ nvm install 7
+ $ npm init -y # 初始化一个配置
+ $ npm install koa # 安装依赖
+ ```
+- 入口文件 `index.js` 添加代码:
+ ```js
+ const Koa = require('koa');
+ const app = new Koa();
+
+ app.use(async ctx => {
+ ctx.body = 'Hello World';
+ });
+
+ app.listen(3000);
+ ```
+- 使用以下命令运行应用程序
+ ```bash
+ $ node index.js
+ ```
+
+
+### 级联
+
+
+```js
+const Koa = require('koa');
+const app = new Koa();
+// X-Response-Time x 响应时间
+app.use(async (ctx, next) => {
+ const start = Date.now();
+ await next();
+ const ms = Date.now() - start;
+ ctx.set('X-Response-Time', `${ms}ms`);
+});
+// 记录器 logger
+app.use(async (ctx, next) => {
+ const start = Date.now();
+ await next();
+ const ms = Date.now() - start;
+ console.log(
+ `${ctx.method} ${ctx.url} - ${ms}`
+ );
+});
+// 响应 response
+app.use(async ctx => {
+ ctx.body = 'Hello World';
+});
+app.listen(3000);
+```
+
+### 配置
+
+:- | :-
+:- | :-
+`app.env` | 默认为 `NODE_ENV` 或 `development`
+`app.keys` | 签名 `cookie` 密钥数组
+`app.proxy` | 何时信任真正的代理头字段
+`app.subdomainOffset` | 要忽略的 `.subdomains` 的偏移量,默认为 `2`
+`app.proxyIpHeader` | 代理 `ip` 头,默认为 `X-Forwarded-For`
+`app.maxIpsCount` | 从代理 `ip` 头读取的最大 `ips` 数,默认为 `0`(表示无穷大)
+
+
+### app.callback()
+
+
+:- | :-
+:- | :-
+`app.listen(...)` [#](https://koajs.com/#app-listen-) | 为一个绑定 `3000` 端口的简单 `Koa` 应用
+`app.callback()` [#](https://koajs.com/#app-callback-) | 返回一个适合 `http.createServer()` 方法的回调函数用来处理请求
+`app.use(function)` [#](https://koajs.com/#app-use-function-) | 添加指定的中间件,详情请看 [Middleware](https://github.com/koajs/koa/wiki#middleware)
+`app.keys` [#](https://koajs.com/#app-keys-) | 设置签名 `cookie` 密钥
+`app.context` [#](https://koajs.com/#app-context) | 从中创建 `ctx` 的原型
+
+
+### 错误处理
+
+```js
+app.on('error', (err, ctx) => {
+ log.error('server error', err, ctx)
+});
+```
+
+默认情况下 `Koa` 会将所有错误信息输出到 `stderr`, 除非 `app.silent` 是 `true`。当 `err.status` 是 `404` 或者 `err.expose` 时,默认错误处理程序也不会输出错误
+
+### Context 示例
+
+```js
+app.use(async ctx => {
+ ctx; // 这是上下文 Context
+ ctx.request; // 这是 koa Request
+ ctx.response; // 这是 koa Response
+});
+```
+
+### app.listen(...)
+
+
+```js
+const Koa = require('koa');
+const app = new Koa();
+app.listen(3000);
+```
+
+`app.listen(...)` 实际上是以下代码的语法糖:
+
+```js
+const http = require('http');
+const Koa = require('koa');
+const app = new Koa();
+http.createServer(app.callback()).listen(3000);
+```
+
+这意味着您可以同时支持 `HTTPS` 和 `HTTPS`,或者在 `多个端口` 监听同一个应用
+
+```js
+const http = require('http');
+const https = require('https');
+const Koa = require('koa');
+const app = new Koa();
+http.createServer(app.callback()).listen(3000);
+https.createServer(app.callback()).listen(3001);
+```
+
+### ctx.throw 示例
+
+```js
+ctx.throw(400);
+ctx.throw(400, 'name required');
+ctx.throw(400, 'name required', { user: user });
+```
+
+`this.throw('name required', 400)` 等价于
+
+```js
+const err = new Error('name required');
+err.status = 400;
+err.expose = true;
+throw err;
+```
+
+### ctx.assert 示例
+
+```js
+ctx.assert(
+ ctx.state.user,
+ 401,
+ 'User not found. Please login!'
+);
+```
+
+### Context(上下文) API
+
+
+:- | :-
+:- | :-
+`ctx.req` | Node 的 request 对象
+`ctx.res` | Node 的 response 对象
+`ctx.request` | Koa 的 Request 对象
+`ctx.response` | Koa 的 Response 对象
+`ctx.state` | 推荐的命名空间,用于通过中间件传递信息到前端视图
+`ctx.app` | 应用实例引用
+`ctx.app.emit` | 发出由第一个参数定义的类型的事件
+`ctx.cookies.get(name, [options])` | 获得 `cookie` 中名为 `name` 的值
+`ctx.cookies.set(name, value, [options])` | 设置 `cookie` 中名为 `name` 的值
+`ctx.throw([status], [msg], [properties])` | 抛出包含 `.status` 属性的错误,默认为 `500`
+`ctx.assert(value, [status], [msg], [properties])` | 当 `!value` 时, `Helper` 方法抛出一个类似 `.throw()` 的错误
+`ctx.respond` | 避免使用 `Koa` 的内置响应处理功能,您可以直接赋值 `this.repond = false`
+
+
+### ctx.cookies.set 参数
+
+:- | :-
+:- | :-
+`maxAge` | 表示从Date开始的毫秒数 `now()` 到期。
+`expires` | 一个 `Date` 对象,指示 `cookie` 的到期日期(默认情况下在会话结束时到期)
+`path` | 表示 `cookie` 路径的字符串(默认为`/`)
+`domain` | 表示 `cookie` 的域的字符串(无默认值)
+`secure` | 一个布尔值,指示 `cookie` 是否只通过HTTPS发送(HTTP默认为false,HTTPS默认为true)。阅读有关此选项的更多信息
+`httpOnly` | 一个布尔值,指示cookie是否只通过HTTP(S)发送,而不可用于客户端 JavaScript(默认为true)
+`sameSite` | 一个布尔值或字符串,指示cookie是否为“同一站点”cookie(默认为false)。这可以设置为“strict”、“lax”、“none”或true(映射为“strect”)
+`signed` | 一个布尔值,指示是否对cookie进行签名(默认为false)。如果这是真的,还将发送另一个附加了.sig后缀的同名cookie,其中一个27字节的url安全base64 SHA1值表示cookie name=cookie值相对于第一个Keygrip键的哈希值。此签名密钥用于在下次收到cookie时检测篡改
+`overwrite` | 一个布尔值,指示是否覆盖以前设置的同名 `cookie`(默认为false)。如果为true,则在设置此Cookie时,将从set-Cookie标头中筛选出在同一请求期间设置的具有相同名称的所有Cookie(无论路径或域如何)
+
+
+### 请求(Request)
+
+
+:- | :-
+:- | :-
+`request.header` | 请求头对象
+`request.header=` | 设置请求头对象
+`request.headers` | 请求头对象。等价于 request.header.
+`request.headers=` | 设置请求头对象。 等价于request.header=.
+`request.method` | 请求方法
+`request.method=` | 设置请求方法, 在实现中间件时非常有用,比如 methodOverride()
+`request.length` | 以数字的形式返回 request 的内容长度(Content-Length),或者返回 undefined。
+`request.url` | 获得请求url地址.
+`request.url=` | 设置请求地址,用于重写url地址时
+`request.originalUrl` | 获取请求原始地址
+`request.origin` | 获取URL原始地址, 包含 protocol 和 host
+`request.href` | 获取完整的请求URL, 包含 protocol, host 和 url
+`request.path` | 获取请求路径名
+`request.path=` | 设置请求路径名并保留当前查询字符串
+`request.querystring` | 获取查询参数字符串(url中?后面的部分),不包含?
+`request.querystring=` | 设置原始查询字符串
+`request.search` | 获取查询参数字符串,包含?
+`request.search=` | 设置原始查询字符串
+`request.host` | 获取 host (hostname:port)。 当 app.proxy 设置为 true 时,支持 X-Forwarded-Host
+`request.hostname` | 获取 hostname。当 app.proxy 设置为 true 时,支持 X-Forwarded-Host。
+`request.URL` | 获取 WHATWG 解析的对象.
+`request.type` | 获取请求 Content-Type,不包含像 "charset" 这样的参数。
+`request.charset` | 获取请求 charset,没有则返回 `undefined`
+`request.query` | 将查询参数字符串进行解析并以对象的形式返回,如果没有查询参数字字符串则返回一个空对象
+`request.query=` | 根据给定的对象设置查询参数字符串
+`request.fresh` | 检查请求缓存是否 "fresh"(内容没有发生变化)
+`request.stale` | 与 req.fresh 相反
+`request.protocol` | 返回请求协议,"https" 或者 "http"
+`request.secure` | 简化版 this.protocol == "https",用来检查请求是否通过 TLS 发送
+`request.ip` | 请求远程地址,当 app.proxy 设置为 true 时,支持 X-Forwarded-Host
+`request.ips` | 当 X-Forwarded-For 存在并且 app.proxy 有效,将会返回一个有序(从 upstream 到 downstream)ip 数组
+`request.subdomains` | 以数组形式返回子域名
+`request.is(types...)` | 检查请求所包含的 "Content-Type" 是否为给定的 type 值
+`request.accepts(types)` | 检查给定的类型 types(s) 是否可被接受,当为 true 时返回最佳匹配,否则返回 false
+`request.acceptsEncodings(encodings)` | 检查 `encodings` 是否可以被接受,当为 `true` 时返回最佳匹配,否则返回 `false`
+`request.acceptsCharsets(charsets)` | 检查 `charsets` 是否可以被接受,如果为 `true` 则返回最佳匹配,否则返回 `false`
+`request.acceptsLanguages(langs)` | 检查 `langs` 是否可以被接受,如果为 `true` 则返回最佳匹配,否则返回 `false`
+`request.idempotent` | 检查请求是否为幂等(idempotent)
+`request.socket` | 返回请求的socket
+`request.get(field)` | 返回请求头
+
+
+### 响应(Response)
+
+
+:- | :-
+:- | :-
+`response.header` | Response header 对象
+`response.headers` | Response header 对象。等价于 response.header.
+`response.socket` | Request socket.
+`response.status` | 获取响应状态。 默认情况下,response.status设置为404,而不像node's res.statusCode默认为200。
+`response.status=` | 通过数字设置响应状态
+`response.message` | 获取响应状态消息。默认情况下, response.message关联response.status。
+`response.message=` | 将响应状态消息设置为给定值。
+`response.length=` | 将响应Content-Length设置为给定值。
+`response.length` | 如果 Content-Length 作为数值存在,或者可以通过 ctx.body 来进行计算,则返回相应数值,否则返回 undefined。
+`response.body` | 获取响应体。
+`response.body=` | 设置响应体为如 `string`,`Buffer`,`Stream`,`Object\|Array`,`null`
+`response.get(field)` | 获取 response header 中字段值,field 不区分大小写
+`response.set(field, value)` | 设置 response header 字段 field 的值为 value
+`response.append(field, value)`| 添加额外的字段field 的值为 val
+`response.set(fields)` | 使用对象同时设置 response header 中多个字段的值
+`response.remove(field)` | 移除 response header 中字段 filed
+`response.type` | 获取 response Content-Type,不包含像"charset"这样的参数
+`response.type=` | 通过 mime 类型的字符串或者文件扩展名设置 response Content-Type
+`response.is(types...)` | 跟 `ctx.request.is()` 非常类似。用来检查响应类型是否是所提供的类型之一
+`response.redirect(url, [alt])` | 执行 [302] 重定向到对应 url
+`response.attachment([filename])` | 设置 "attachment" 的 Content-Disposition,用于给客户端发送信号来提示下载
+`response.headerSent` | 检查 response header 是否已经发送,用于在发生错误时检查客户端是否被通知。
+`response.lastModified` | 如果存在 Last-Modified,则以 Date 的形式返回。
+`response.lastModified=` | 以 UTC 格式设置 Last-Modified。您可以使用 Date 或 date 字符串来进行设置。
+`response.etag=` | 设置 包含 "s 的 ETag
+`response.vary(field)` | 不同于field.
+`response.flushHeaders()` | 刷新任何设置的响应头,并开始响应体
+
+
+### 请求(Request)别名
+
+以下访问器和别名与 [Request](#请求request) 等价:
+
+- `ctx.header`
+- `ctx.headers`
+- `ctx.method`
+- `ctx.method=`
+- `ctx.url`
+- `ctx.url=`
+- `ctx.originalUrl`
+- `ctx.origin`
+- `ctx.href`
+- `ctx.path`
+- `ctx.path=`
+- `ctx.query`
+- `ctx.query=`
+- `ctx.querystring`
+- `ctx.querystring=`
+- `ctx.host`
+- `ctx.hostname`
+- `ctx.fresh`
+- `ctx.stale`
+- `ctx.socket`
+- `ctx.protocol`
+- `ctx.secure`
+- `ctx.ip`
+- `ctx.ips`
+- `ctx.subdomains`
+- `ctx.is()`
+- `ctx.accepts()`
+- `ctx.acceptsEncodings()`
+- `ctx.acceptsCharsets()`
+- `ctx.acceptsLanguages()`
+- `ctx.get()`
+
+### 响应(Response)别名
+
+以下访问器和别名与 [Response](#响应response) 等价:
+
+- `ctx.body`
+- `ctx.body=`
+- `ctx.status`
+- `ctx.status=`
+- `ctx.message`
+- `ctx.message=`
+- `ctx.length=`
+- `ctx.length`
+- `ctx.type=`
+- `ctx.type`
+- `ctx.headerSent`
+- `ctx.redirect()`
+- `ctx.attachment()`
+- `ctx.set()`
+- `ctx.append()`
+- `ctx.remove()`
+- `ctx.lastModified=`
+- `ctx.etag=`
+
+### request.fresh 示例
+
+```js
+// freshness 检查需要状态 20x 或 304
+ctx.status = 200;
+ctx.set('ETag', '123');
+
+// 缓存正常
+if (ctx.fresh) {
+ ctx.status = 304;
+ return;
+}
+
+// 缓存已过时
+// 获取新数据
+ctx.body = await db.find('something');
+```
+
+### ctx.is 示例
+
+```js
+// Content-Type: text/html; charset=utf-8
+ctx.is('html'); // => 'html'
+ctx.is('text/html'); // => 'text/html'
+ctx.is('text/*', 'text/html');
+// => 'text/html'
+// 当 Content-Type 为 application/json 时
+ctx.is('json', 'urlencoded'); // => 'json'
+ctx.is('application/json');
+// => 'application/json'
+ctx.is('html', 'application/*');
+// => 'application/json'
+
+ctx.is('html'); // => false
+```
+
+### ctx.accepts 示例
+
+```js
+// 接受: text/*, application/json
+ctx.accepts('html');
+// => "html"
+ctx.accepts('text/html');
+// => "text/html"
+ctx.accepts('json', 'text');
+// => "json"
+ctx.accepts('application/json');
+// => "application/json"
+```
+
+### request.acceptsCharsets 示例
+
+```js
+// Accept-Charset: utf-8, iso-8859-1;q=0.2, utf-7;q=0.5
+ctx.acceptsCharsets('utf-8', 'utf-7');
+// => "utf-8"
+
+ctx.acceptsCharsets(['utf-7', 'utf-8']);
+// => "utf-8"
+```
+
+检查 `charsets` 是否可以被接受,如果为 `true` 则返回最佳匹配, 否则返回 `false`
+
+### response.set 示例
+
+```js
+ctx.set({
+ 'Etag': '1234',
+ 'Last-Modified': date
+});
+```
+
+使用对象同时设置 response header 中多个字段的值
+
+### response.type 示例
+
+```js
+const ct = ctx.type;
+// => "image/png"
+```
+
+获取 response Content-Type,不包含像"charset"这样的参数
\ No newline at end of file
diff --git a/scripts/assets/expressjs.svg b/scripts/assets/expressjs.svg
new file mode 100644
index 0000000..c1e0224
--- /dev/null
+++ b/scripts/assets/expressjs.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/scripts/assets/koajs.svg b/scripts/assets/koajs.svg
new file mode 100644
index 0000000..f58555c
--- /dev/null
+++ b/scripts/assets/koajs.svg
@@ -0,0 +1 @@
+
\ No newline at end of file