Skip to content

valfaw/moco

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

3 Commits
 
 
 
 
 
 

Repository files navigation

What is moco?

'moco' is a tool which allows mocking REST APIs with ease.

The idea of developing moco came from a time where I worked almost exclusively as frontend developer in an enviroment where the backend was extremely unstable and specifications changed from day to day. There was always the need of developing the client with no backend to test against, so with moco you can quickly define a new endpoint in the API and use it to help you develop the client.

Furthermore, it gives you the option to mask the "real" server, in other words, you can define a new endpoint for your existing API, set moco as your backend and moco will manage to pass through any other request that is not specifically directed to it, acting basically as a proxy.

DISCLAIMER

Be aware that moco is only meant to be used in a development enviroment and right now it is not much more than a pet project, it covers my needs for my current enviroment, which is unencrypted HTTP/1.1. So expect some kind of problem if your enviroment has different conditions.

However if I feel it is useful, I will continue improving it and adding new features when I find the time, (feel free to open issues describing any kind of problem you may encounter).

Prerequisites

nodejs v12.10.0, with any other version is untested

How moco works?

'moco' will use a series of defined endpoints to match the incoming requests and based on the user configuration, it will capture the request and return a mocked response or it will redirect the request to its intended destiny

Three fields are used to capture the request:

  1. Pathname: Required, only the pathname, the host is not needed and at the moment the query params are ignored
  2. Method: Required, the HTTP method, there is no much explanation needed
  3. Headers: Optional, two different things about a header can be checked to match a request. One, Is the header present? And two, if is in fact present, do its value match the expected one?

Configuration data model

'moco' behaviour will be directed through a JSON configuration file, which is based in the following data model, but before diving in the model just a couple of considerations:

  1. This model is not described in any standard notation (to my knowledge), is just a custom way of quickly expressing objects without too much nuisance

  2. There are some symbols used that could be misinterpreted so here is its meaning

    • alias => type(regex) represents a type alias, used for subsets of the primitive types
    • ? means optional field
    • (a | b) it is just an or over the types
statusCode => integer(100-999)

headers {
    string key1
    string key2
    ...
    string keyN
}

request {
    string path
    string method
    headers headers?
}

response {
    statusCode statusCode
    headers headers?
    (object | string) body?
}

endpoint {
    request request
    array(response) responses
}

config {
    integer port
    string maskedHost?
    boolean isActive?
    statusCode activeResponse?
    array(endpoint) endpoints
}

Example of an actual configuration file (generated by moco, see how in the usage section):

{
    "port": 8080,
    "maskedHost": "example.com",
    "endpoints": [
        {
            "request": {
                "path": "path/to/{*}/endpoint",
                "method": "GET",
                "headers": {
                    "Accept": "*/*"
                }
            },
            "responses": [
                {
                    "statusCode": 200,
                    "headers": {
                        "Content-Type": "application/json",
                        "User-Agent": "moco/0.1"
                    },
                    "body": {
                        "message": "Success!"
                    }
                },
                {
                    "statusCode": 503,
                    "headers": {
                        "Content-Type": "application/json",
                        "User-Agent": "moco/0.1"
                    },
                    "body": {
                        "message": "Unavailable"
                    }
                }
            ]
        }
    ]
}

Particularities and caveats of the model

Definition of paths

To capture the incoming request you only have to write the path and the method used by the request. But if there is some case that the path is parameterized there is something you can do to avoid using the exact same URL with every request.

For example, let's say that the path of interest is user/1/detail. This path usually makes reference to an entity user identified by the id 1. To avoid having to remember ids or any kind of parameter, you could set the request to be captured with a path like the following, and moco will manage to match the incoming request

user/{*}/detail

where {*} can be any caracter in the unreserved group for URIs defined in RFC 3986. Just to clarify, the regex that should be honored is /[\w-.~]+/, and here is the excerpt of the aformentioned RFC talking about unreserverd characters

Create regexp to match incoming requests by path conforming
RFC 3986: https://www.ietf.org/rfc/rfc3986.txt
2.3.  Unreserved Characters
        Characters that are allowed in a URI but do not have a reserved
        purpose are called unreserved.  These include uppercase and lowercase
        letters, decimal digits, hyphen, period, underscore, and tilde.

        unreserved  = ALPHA / DIGIT / "-" / "." / "_" / "~"

Headers

The headers object is somewhat special, the usual way of expresing a set of headers should be something like an array (or whatever structure you feel like using) of objects similar to this

header {
    string name
    string value
}

But here, I am making use of the way in which javascript defines objects to ease the writing of the configuration file (and incidentally, sometimes, the code). Each one of the keys (key1, key2, ..., keyN) is a value in itself, the name of the header, and the value will be the actual value of header. An example of this can be seen in the previous example.

Another thing to notice is the treatment of the values in the headers object. If the value null, moco will only check the existence of the header in the request, but if the value is a string, moco will check if the string defined in the configuration file is contained in the header of the incoming request.

HTTP status code range

Regarding HTTP status codes, the same condition as nodejs is enforced. In nodejs documentation, an invalid HTTP status code is defined as follows

ERR_HTTP_INVALID_STATUS_CODE: Status code was outside the regular status code range (100-999).

(Ref: https://nodejs.org/dist/latest-v12.x/docs/api/errors.html#errors_err_http_invalid_status_code)

isActive and activeResponse properties

As specified in the data model isActive and activeResponse are optional, if not present 'moco' will fill up its values for you. By default isActive will be set to true and activeResponse will be set to the status code of the first response defined in the configuration file.

Set a non defined status code as active response

'moco' will print a warning to its console but it will allow it. The returned response will be empty, no headers and no body.

Usage

usage: node moco.js [help | run <path> | template [<path>]]

or making moco.js exectuable

usage: node <path_to_moco.js> [help | run <path> | template [<path>]]

Options

  • help

    Prints all available options to run moco the output will be like this:

      usage: node moco.js [help | run <path> | template [<path>]]
      help
          Print this message
      run <path>, <path> : string
              Run moco using configuration file stored in <path>
      template [<path>], <path> : string
              Convenience command, saves a configuration template file to <path>.
              If <path> not specified `config.moco.json` will be created in the current directory
    
  • template

    Creates a configuration file with the expected format which can be easilied tailored to any API. It will be saved to config.moco.json in the current directory unless a path is specified.

Console commands

  • help

    Prints the available commands once moco is running. Output:

      help
          Print the list of moco commands available
      list
              List all endpoints
      info <id>, <id> : integer
              Print the endpoint <id> information in readable way
      toggle <id>, <id> : integer
              Enable/Disable endpoint <id>
      response <id> <HTTPStatusCode>, <id> : integer, <HTTPStatusCode> : {100 - 999}
              Set endpoint <id> to answer with status code <HTTPStatusCode>
    
  • list

    List all the endpoits loaded through the configuration file and its current state, i.e. which response is returning and if is active. Also the masked host will be printed if present

  • info <id>, <id> : integer

    Prints in a human friendly way the information associated to the endpoint <id>. These ids are autogenerated by moco and can be found with the list command.

  • toggle <id>, <id> : integer

    Enable/Disable the endpoint <id>.

  • response <id> <HTTPStatusCode>, <id> : integer, <HTTPStatusCode> : {100 - 999}

    Set the endpoint <id> to answer with the status code <HTTPStatusCode>. As mentioned before, if there are several responses with the same status code, the first one will be picked and if there is no response at all, moco will return an empty response.

License

Non-Profit Open Software License 3.0 (NPOSL-3.0)

About

A tool to mock REST APIs

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published