Notoriously Punctual Manatee

    @36node/mock-server
    TypeScript icon, indicating that this package has built-in type declarations

    0.1.18 • Public • Published

    @36node/mock-server

    version downloads

    mock-server 基于 json-server, 为了更好的提供数据 mock 服务.

    Install

    $ yarn install @36node/mock-server

    Use

    1. 在 Nodejs 中使用

    #!/usr/bin/env node
    
    const mockServer = require("@36node/mock-server");
    
    const app = mockServer({
      db: {
        pets: [
          { id: 1, name: "kitty", tag: "CAT", grade: 3 },
          { id: 2, name: "pi", tag: "DOG", grade: 4 },
        ],
      },
      rewrites: {
        "/store/pets*": "/pets$1",
      },
      routers: [], // custom middle ware
      aggregations: {
        "/pets": {
          grade: records => _.sumBy(records, "grade") / records.length,
          count: records => records.length,
        },
      },
    });
    
    app.listen(3000, () => {
      console.log("JSON Server is running on port 3000");
    });

    2. 在 webpack develop server 中使用

    使用 react-app-rewired 时, 通过 config-overwrites.js 文件 配置 devServer

    const stopMock = process.env.MOCK === "false" || process.env.MOCK === "FALSE";
    const defaultServerOpts = { delay: 500 };
    const {
      serverOpts = defaultServerOpts,
      db: {
        pets: [
          { id: 1, name: "kitty", tag: "CAT", grade: 3 },
          { id: 2, name: "pi", tag: "DOG", grade: 4 },
        ],
      },
      rewrites: {
        "/store/pets*": "/pets$1",
      },
      routers: [], // custom middle ware
      aggregations: {
        "/pets": {
          grade: records => _.sumBy(records, "grade") / records.length,
          count: records => records.length,
        },
      },
    } = someMockConfig;
    
    const mockServer = require("@36node/mock-server");
    
    module.exports = {
      ...otherConfig,
    
      devServer: function(configFunction) {
        return function(proxy, allowedHost) {
          const config = configFunction(proxy, allowedHost);
    
          if (stopMock) {
            return config;
          }
    
          /**
           * mock server hoc
           * @param {Express.Application} app
           */
          function configMock(app) {
            // 根据 请求的 header.accept 的类型决定是正常渲染,还是进入mock-server
            const shouldMockReq = req => {
              return (
                req.method !== "GET" ||
                (req.headers.accept &&
                  req.headers.accept.indexOf("application/json") !== -1)
              );
            };
    
            if (serverOpts.delay) {
              app.use((req, res, next) => {
                if (shouldMockReq(req)) {
                  return pause(serverOpts.delay)(req, res, next);
                }
                return next();
              });
            }
    
            mockServer({ app, db, rewrites, routers, shouldMockReq });
    
            return app;
          }
    
          const prev = config.before;
    
          config.before = compose(
            configMock,
            app => {
              prev(app);
              return app;
            }
          );
    
          return config;
        };
      },
    };

    Api

    mockServer(opts)

    params:

    1. opts: Object

      db: // 同 json-server 的 db 配置 https://github.com/typicode/json-server#getting-started

      rewrites (Optional): 同 json-server https://github.com/typicode/json-server#rewriter-example

      routes (Optional): [Express.Middleware] 同 json-server custom-middle https://github.com/typicode/json-server#add-middlewares

      aggregations (Optional): Object 见下文 Aggregation

      app (Optional): Express.Application, 如果没有则自动新建

      shouldMock (Optional): (req, res) => Boolean, 判断 request 是否使用 mock-server 的 中间件

    返回:Express.Appliction

    Array

    使用标准 url query 格式传递数组数据

    a=1&a=2
    

    Filter

    Use . to access deep properties

    GET /posts?title=json-server&author=typicode
    GET /posts?id=1&id=2
    GET /comments?author.name=typicode
    

    Paginate

    Use _offset and optionally _limit to paginate returned data. (an X-Total-Count header is included in the response)

    In the Link header you'll get first, prev, next and last links.

    GET /posts?_offset=10
    GET /posts?_offset=7&_limit=20
    

    note: 10 items are returned by default

    Sort

    Add _sort and _order (ascending order by default)

    # asc
    GET /posts?_sort=views
    
    # desc
    GET /posts/1/comments?_sort=-votes
    

    note: list posts by views ascending order and comments by votes descending order

    For multiple fields, use the following format:

    GET /posts/1/comments?_sort=-votes&_sort=likes
    

    _prefixing a path with - will flag that sort is descending order. When a path does not have the - prefix, it is ascending order.

    Operators

    Add _gt, _lt, _gte or _lte for getting a range

    GET /posts?views_gte=10&views_lte=20
    

    Add _ne to exclude a value

    GET /posts?id_ne=1
    

    Add _like to filter (RegExp supported)

    _like support array

    GET /posts?title_like=server
    

    Select

    Specifies which document fields to include or exclude

    GET /posts?_select=title&_select=body
    GET /posts?_select=-comments&_select=-views
    

    or

    _select=title,body
    

    prefixing a path with - will flag that path as excluded. When a path does not have the - prefix, it is included A projection must be either inclusive or exclusive. In other words, you must either list the fields to include (which excludes all others), or list the fields to exclude (which implies all other fields are included).

    Aggregation

    聚合的 query 请求。聚合请求通过 _group 和 _select 参数来控制,通过 opts.aggregations 配置:

    比如对于一个 db 配置:

    const faker = require("faker");
    const _ = require("lodash");
    const moment = require("moment");
    
    const now = moment();
    
    const generate = count =>
      _.range(count).map((val, index) => {
        const birthAt = faker.date.between(
          moment()
            .subtract(10, "year")
            .toDate(),
          moment()
            .subtract(1, "year")
            .toDate()
        );
    
        const age = now.diff(moment(birthAt), "year");
    
        return {
          id: faker.random.uuid(), // pet id
          name: faker.name.lastName(), // pet name
          tag: faker.random.arrayElement(["CAT", "DOG"]), // pet tag
          owner: faker.name.firstName(), // pet owner
          grade: faker.random.number({ min: 1, max: 5 }), // pet grade
          age, // pet age
          birthAt: birthAt.toISOString(), // pet birth time
        };
      });
    
    const db = {
      pets: generate(100),
    };

    其中包括了 100 个 pets 的 mock 数据,可使用的路由有:

    GET    /pets
    GET    /pets/{petId}
    POST   /pets
    PUT    /pets/{petId}
    PATCH  /pets/{petId}
    DELETE /pets/{petId}
    

    聚合只在 GET /pets 中有效

    简单分组

    如果需要统计 pets 中猫和狗的数量, 可以对 tag 分组

    配置 aggregations 参数

      aggregations: {
        "/pets": {
          // records 是分组后的数据集合
          count: records => records.length,
          // 默认支持 两种聚合简写 求和 'sum' 和 平均 ‘avg'
          grade: 'avg',
        },
      },

    请求:

    GET /pets?_group=tag
    

    结果:

    [
      {
        "id": "tag=CAT",
        "tag": "CAT",
        "grade": 3.017857142857143,
        "count": 56
      },
      {
        "id": "tag=DOG",
        "tag": "DOG",
        "grade": 3.3863636363636362,
        "count": 44
      }
    ]

    按时间粒度分组

    如果需要统计每个月分别生了多少猫和狗, 可以按 tag 和 birthAt.month 分组

    对于时间的分组条件,可以采用不同粒度进行分组,query 的格式为 birthAt.month 表示在 birthAt 字段上 按照 月粒度进行分组。

    支持的粒度包括:

    [
      "year", // 年
      "quarter", // 季度
      "month", // 月
      "week", // 星期
      "isoWeek", // iso 星期
      "day", // 天
      "hour", // 小时
      "min", // 分钟
      "second", // 秒
    ];

    请求:

    GET /pets?_group=tag&_group=birthAt.month
    

    结果:

    [
      ...,
      {
        "id": "tag=CAT&birthAt=2012-12-31T16%3A00%3A00.000Z",
        "tag": "CAT",
        "birthAt": "2012-12-31T16:00:00.000Z",
        "count": 6
      },
      {
        "id": "tag=DOG&birthAt=2014-12-31T16%3A00%3A00.000Z",
        "tag": "DOG",
        "birthAt": "2014-12-31T16:00:00.000Z",
        "count": 7
      }
    ]

    Tips:

    1. 在返回结果中,birthAt 当前月的起始时间(UTC),如果使用其他粒度,则类似。
    2. 如果同时传入统一字段的多个时间粒度,比如 _group=birthAt.year&_group=birthAt.month, 则较小的时间粒度(month)会生效.

    Keywords

    none

    Install

    npm i @36node/mock-server

    DownloadsWeekly Downloads

    393

    Version

    0.1.18

    License

    MIT

    Unpacked Size

    46.4 kB

    Total Files

    10

    Last publish

    Collaborators

    • zzswang
    • hauy
    • 36node-robot
    • lidongbest5