Run a local mock server in your project
Tags: mock, server, javascript, fake
Published: 2022-02-28 08:06:30

> At first, i'm not a native english speaker, so forgive me for my poor english, but i still try to write the article as accurately as possible.

> I'm the author of the fake data library `suchjs`, recently i add a new feature to the command line tool `such-cli` what is based on `suchjs`, that is, to run a local mock server by executing the command `npx such serve`.  

We often encounter some projects with separated front-back ends. Most time, the front end engineers start the working when the backend's APIs are not ready yet, so we need mock the APIs with data formats what are agreed by both front-end and back-end engineer.

Some open source like `YApi` can build a web platform for doing this. You can did good managements by using it, but the whole process from deploying the platform to creating the mock APIs is a bit complicated. If you just need handle a few  APIs and need more customization, the `such-cli` will provide another choice.

Now i will introduce how to use it to run a mock server in your local project.

**The first step:** 

Install the such-cli package.

```bash
# npm
npm install --save-dev such-cli
# yarn
yarn add -D such-cli
# pnpm
pnpm add -D such-cli
```

**The second step:**

Initialize the `suchjs` config.

```bash
npx such init
```

You just need configure step by step according to the question prompts.

After that, you will get a directory structure like this(the default config):

```bash
|- such.config.js # the suchjs config file
|- suchas/    # the root directory of suchjs files
|---- data/ # save the data file such as dicts and json
|---- server/ # save the mock template file
```

**The third step:**

Start the mock server.

```bash
npx such serve -p 8181 -w
```
After that, a node server will running at your localhost: [http://localhost:8181](http://localhost:8181).

The `-w` option means `--watch`, will watch the config file `such.config.js`'s change and restart the server with a hot reload.

Now the mock server is startup, but it can't accept any request yet. Suppose you have a request named `http://localhost:8181/api/v1/user/list`, and you need to return a json response like this:

```json
{
  "errno": 0,
  "errmsg": "",
  "data": {
     "total": 1000,
     "users": [{
        id: 1,
        firstName: "abc",
        lastName: "def",
        fullName: "abc def",
        email: "xxx@gmail.com",
        mobile: "xxx"         
     },{
        "...": "more data"
     }]
  }
}
```

Or an error occurred:
```json
{
  "errno": 1,
  "errmsg": "some message"
}
```

Now let's do some analysis:

- The `/api/v1` is a global prefix of our most requests.
- The `/api/v1/user/list` is a `get` request and non restful.

Then let's do some configure for the `such.config.js`:

```javascript
// such.config.js
module.exports = {
   // ...
   config: {
     server: {
        prefix: ["/api/v1", {
           // some APIs with pathname not begin with `/api/v1` 
           // it's similar to nestjs `setGlobalPrefix`
           exclude: []
        }],
        // use a dot '.' for joining the pathname segments 
        pathSegSplit: "."
     }
   }
};
```

Since the request's pathname is `/api/v1/user/list`, we remove the global prefix `/api/v1` from it, the remaining path becomes to `user/list`, so we join the two segments `user` and `list` of the path with the dot '.' (`pathSegSplit`), we get a file name without extension `user.list`.

At last, we know that the request is of json data type, so we choose the `.json` as the file's extension name. This can be configured in the `such.config.js`'s `extContentTypes` field.

```javascript
// such.config.js
module.exports = {
   server: {
      extContentTypes: {
         ".json": "application/json",
         ".js": ["text/javascript", "application/javascript"]
      }
   }
}
```

If the requested content-type matches the corresponding extension content-type list in the `extContentTypes`, the extension will be used for the file name at last, otherwise The extension list in the configuration `extensions` will be combined with the file name to form the final file path to be searched one by one.

```javascript
// such.config.js
module.exports = {
   // ...
   extensions: [".json", ".js", ".txt"],
   server: {
      // ...
   }
}
```

Back to the topic, we get the file name `user.list.json` at last. So we create it under the `server` directory.

```bash
cd ./suchas/server
touch user.list.json
```  

Then we edit the file `user.list.json` as a mock data template(the fake data rule is based on `suchjs`):

```json
// suchas/server/user.list.json
{
  "errno:{1}": [0, 1],
  "errmsg:{1}": ["", ":string:{10,30}"],
  "data?": {
     "total": ":int:[1000,2000]",
     "users{3,10}": {
         "id": ":increment"
         "firstName": ":lowercase:{3,8}",
         "lastName": ":lowercase:{3,8}",
         "fullName": ":::`:ref:&./firstName` `:ref:&./lastName`",
         "email": ":email:#[domain='gmail.com']",
         "mobile": ":regexp:/(\\([0-9]{3}\\)|[0-9]{3}-)[0-9]{3}-[0-9]{4}/" 
     }
  }
}
```

Now we open the url [http://localhost:8181/api/v1/user/list](http://localhost:8181/api/v1/user/list) in the browser, we will see the response json data at the page.

But at the moment, the json data format may not exactly what we wanted. We need do some customization to make the data more exactly.

Go on editing the `such.config.js`:

```javascript
// such.config.js
module.exports = {
  server: {
     // ...
     buildConfig(pathname, ctx, config){
        if(pathname === "user/list"){
           const t = +new Date;
           if(t % 5 === 1){
              // random response an error
              return {
                 instance: {
                    keys: {
                      "/errno": {
                         index: 1
                      },
                      "/errmsg": {
                         index: 1
                      },
                      "/data": {
                         exists: false
                      }
                    } 
                 }
              };
           }
           const min = ctx.query('page') < 10 ? 10 : 3;
           return {
               timeout: [200, 500],
               instance: {
                  keys: {
                    "/errno": {
                       index: 0
                    },
                    "/errmsg": {
                       index: 0
                    },
                    "/data": {
                       exists: true,
                    },
                    "/data/users": {
                       min
                    }
                  } 
               }
           };
        }
     }
  }
};
```

After saving the config, refresh the page, now the returned json data is closer to our needs at last. 

At the end, we extract some common data types into the configuration so we can use them in all the mock data template files.

```javascript
// such.config.js
module.exports = {
  types: {
     mobile$us: [
       "regexp", 
       "/(\\([0-9]{3}\\)|[0-9]{3}-)[0-9]{3}-[0-9]{4}/"
     ]
  }
};
```
```json
{
   "data?": {
      "users{3,10}": {
         "mobile": ":mobile$us"
      }
   }
}
```

Finally, the mock server worked fine, we just add more mock data template files for more APIs. Maybe you have a restful API, you just need create nested directories like the pathname segments and create files with the method name, such as `get.json`, `post.json`, `put.json`, more details can be seen in the github [such-cli](https://github.com/suchjs/such-cli).

Thanks for taking the time to read this article. Hope it can help someone with mock needs.