Dynamic and stateful Mock Servers

APIGit

2022-10-15

APIGit supports dynamic, stateful and programmable mock servers to simulate any of complex business logic with ease. HTTP request messages are handled by defining routes using mock.define() and providing a URL pattern, HTTP verb and a callback function. For example:

mock.define('/users', 'GET', function(req, res) {
    ...
  })

or, for a POST method:

mock.define('/users', 'POST', function(req, res) {
    ...
  })

Standard HTTP methods such as 'DELETE', 'PUT' and 'OPTIONS' methods are supported. The callback function you provide is always passed two objects as parameters, the Request and Response objects. The Request object contains all the data pertaining to the incoming request such as headers, body payload, and parameters. We use the Response object to build responses to the request.



Let's expand on the example above and start building a basic CRUD service for users. First, lets define a route to return a set of users:

mock.define('/users', 'GET', function(req, res) {
    var users = [ 
      { username: "dave", email: "dave@gmail.com" },
      { username: "john", email: "john@gmail.com" }
    ]
  
    return res.json(users)
  })

The res.json call will send a JSON response to the calling application. Remember to call a response function on the res object, without it your service will timeout and not return a successful result. We'll explore the other available response functions later



Lets add support for filtering based on the user's age using query parameters. req.query is an object containing the parsed query string, defaulting to {}. If we submit a request for 'GET /users?age=30' we can access the age value with req.query.age:

mock.define('/users', 'GET', function(req, res) {
    var users = [
      { username: "dave", email: "dave@gmail.com", age: 32 },
      { username: "john", email: "john@gmail.com", age: 30 }
    ]
  
    if (req.query.age) {
      // convert req.query.age from String to a Number before filtering
      return res.json(_.filter(users, { 'age': Number(req.query.age) }))
    }
  
    return res.json(users)
  })

We use LoDash _.filter function to extract an array of users that match the age provided. If no users match the age criteria an empty array is returned. The full LoDash library is available to use, it provides useful functions for manipulating arrays and objects.



To create our route for retrieving a single user we use route parameters as part of the service's URL path. Route parameters are specified using {route_param_name} syntax, so in our example the URL path to retrieve a user by their username will be/users/{username}. The req.params is an object containing properties mapped to the named route parameters and our username will be available as req.params.username.

mock.define('/users/{username}', 'GET', function(req, res) {
    var users = [
      { username: "dave", email: "dave@gmail.com", age: 32 },
      { username: "john", email: "john@gmail.com", age: 30 }
    ]
  
    // respond with the user or an empty object if user doesnt exist
    return res.json(_.find(users, { 'username': req.params.username }) || {})
  })

Lets add a route for creating users. We'll verify that a username has been sent with the request and respond with an 400 error if it is missing. Data that is sent as a body payload in POST requests is accessible on the req.body object.

mock.define('/users', 'POST', function(req, res) {
    // validate username is present
    if (req.body.username === undefined) {
      return res.json(400, { error: { message: "Missing username" } })
    }
  
    return res.json({ status: "ok" })
  })

By default, all responses return a 200. If a number is provided as the only parameter to res.json a response body string is assigned for you. For example, 400 will respond with "Bad Request". We've specified a custom error message in the above example.



That's a solid start, you can do a lot with services that return simple canned responses but they do only get you so far. Lets move onto adding realistic behaviour to our services by persisting data and dynamically generating responses.