npm package: @mora/foe-api
Description: foe api
Author: Mora
Latest version: 0.6.0

# foe-api

给前端程序员使用的后端接口统一管理工具(支持 TypeScript)

## 背景

在开发一个项目时，后端接口比较分散，没有一个统一的管理工具，如果不看接口使用说明，也无法知道接口需要什么参数，返回什么数据。

所以设计 foe-api 的初衷主要是为了解决上面问题。同时在设计 foe-api 的时候，由于采用了类 koa 的插件机制，可以在一个插件中，对请求之前的数据进行处理，也可以对讲求之后返回的数据进行处理。所以利用这个插件机制，可以使 foe-api 更强大，如：

- 可以实现接口数据缓存
- 可以对返回的数据的键名统一格式化（比如后端返回的键名都是下划线形式的，可以使用插件将其转化成驼峰形式）
- 可以自定义你自己的 fetch 请求（比如可以使用原生的 window.fetch，也可以使用 jquery 的 ajax 等等，只需要将返回的数据放到 Context.body 中即可）
- 可以轻松实现接口 mock

## 使用示例

```ts
import { Application } from 'foe-api'

//========= 初始化 ==========//
let app = new Application({
  /* 配置一：初始化配置 */
})


//========= 使用插件 ==========//
app.use(async (ctx, next) => {    // 类似于 koa 的插件方式，系统默认提供 fetch、mock 等插件
  if (ctx.name === 'getUser') {
    ctx.body = {id: 1, name: 'Alex', age: 20} // 调用接口的时候，会将 body 中的内容返回给调用者
  }
  await next()
})


//========= 定义接口 ==========//
/** 调用接口时需要的参数的定义 */
interface IgetUserParam {
  id: number
}
/** 接口返回的数据定义 */
interface IgetUserReturn {
  id: number
  name: string
  age: number
}
let getUser = app.api<IgetUserParam, IgetUserReturn>('getUser', {
  /* 配置二：定义时的配置 */
  path: '/api/user/:id',
  method: 'GET'
})


//========= 调用接口 ==========//
getUser({id: 0}, { /* 配置三：调用时的配置 */ })
  .then(user => {
    console.log(user) // user 将会是 {id: 1, name: 'Alex', age: 20}
  })
```

## 关于调用接口时传的参数

以上面的 `getUser` 接口为例，第一个参数 `{id: 0}` 会安一定的规则分配给配置中的 `path`、`query`、`body` 三个字段，分配规则是：

- 优先分配给 `path` 中带 `:` 前缀的参数，如上面 path 配置 `/api/user/:id` 中的 `:id`
- 如果有配置 `query`，就会将对应的参数分配给它（上面没有配置 query，所以没有；配置方法见下面的『配置』模块）
- 如果有配置 `body`，则只会分配配置好的参数给它
- 如果没有配置 `body`，并且接口支持配置 `body` (GET 请求不支持配置 `body`)，则会将除去 `path` 和 `query` 中的参数，剩下的所有参数分配给 `body` 提交给后台

## 配置

在使用过程中，可以在三个不同的地方对接口进行配置：

1. 初始化 Application 的时候
2. 使用 Application.api 定义接口的时候
3. 调用接口的时候

三个地方的配置项都是一样的，系统会在调用的时候将三个地方的配置进行合并，传给 Context.config 变量

### 配置项

#### basePath

基本的 path，所有的 path 都会加上此 basePath，默认为空字符串

如，basePath = 'http://backend.com/v1';  path = '/user/:id';

则，最终的 path 为 'http://backend.com/v1/user/:id'

**basePath 中也可以带参数，最终只是会将 basePath 和 path 合并生成新的 path**

#### path

带参数的 url 路径，参数前面需要加 `:` 以区别，如：

获取用户信息接口（需要传 uid 给后端）=> /api/user/:uid

#### method

和在 http 中配置 method 一样，但这里配置的优先级比 http.method 中配置高，所以优先会使用此处配置的值

#### http

window.fetch 相关选项，可以在这里指定 header 等信息


#### query

指定需要提供的 query 参数的一些规则

**如果有 query，一定需要配置 query，否则不会自动将参数添加到 path 中**

* 可以配置成对象形式，如

  ```js
  {
    uid: {required: true, type: 'number', backend: 'userid'},
    version: {required: false, type: 'number', defaultValue: 3}
  }
  ```

  上面的配置表示 `uid` 参数必填，并且组装成 url 时会变成 `userid` 传给后端；而 `version` 参数可以不填，默认值为 `3`。
  如果 `path="/api/user"`，同时使用 `{uid: 10}` 去调用此 api 的话，组装后的 `url` 为 `"/api/user?userid=10&version=3"`

* 由于对象形式的配置太过繁杂，所以支持一种简化的字符串方式的配置，如上面的配置可以配置成：

  ```js
  "uid:number>userid=&version:number=3"
  ```

  规则是：

  - 第一个参数是键名，如 `uid`, `version`
  - `:` 后面的名称表示参数类型，如 `:number`
  - `>` 后面的名称表示传给后端的名称，即 `backend` 配置，如 `>userid`
  - 如果没有 `=`，表示此值是选填的
  - 如果有 `=` ，并且 `=` 后面没有值，表示此值是必填的
  - 如果有 `=`，并且 `=` 后面有值的话，表示此值是选填的，并且 `=` 后面的值即为此参数的默认值


  示例：

  - 'uid'           转化成  uid: { }
  - 'uid='          转化成  uid: { required: true }
  - 'uid:number=3'  转化成  uid: { type: 'number', defaultValue: 3 }
  - 'uid>userid=4'  转化成  uid: { backend: 'userid', required: true, defaultValue: '4' }

  **建议都加上 type，方便在前端做个简单的验证，提前发现错误**

#### body

指定需要提供的 body 参数的一些规则

当没有指定 body 时，会使用用户提供的所有参数，去除 path 中的和 query 中的参数，将剩下的所有参数当作 body 发送

**和 query 配置方法类似，请参考 query**

**如果要发送的 body 不是对象时，可以使用 `__rawBody` 字段**

#### debug

是否启用 debug


#### mock

mock 相关选项

设置成 `false` 表示禁用 mock， `true` 表示启用 mock

另外，也可以设置成对象 `{enable: true, delay: 300}`， 表示此 mock 数据延迟 300 毫秒再返回