Mocking REST endpoints in web apps, the easy way

tldr; Use client-side mocks for your REST API endpoints with connect-api-mocker

Mocking REST endpoints in web apps, the easy way

Why we need mocks?

At one of my previous companies, in the early times, we had a flow like that:

  • A feature implementation is created by design team
  • Designs come to front-end team(us). We create static HTML pages, then task goes to back-end team.
  • Back-end team makes HTML dynamic.
  • Then probably because of we need some re-work and some extra JavaScript codes, the task returns back to front-end team.
  • Then task goes testing team…

After we changed our architecture as a Single Page Application and started to use REST API as the back-end, we wanted to make that flow faster and decided to use client side mocking solutions. The idea was to change our flow like that:

  • Design comes to front-end team
  • Front-end and back-end team decides endpoint structures that they will need for that task.
  • Front-end team uses mocks and starts implementation without waiting for real endpoint
  • Back-end team tries to create that endpoint. When endpoint is ready, front-end team doesn’t need any change for release.

That means, you can develop your task by parallel with your back-end team. This will be improve your productivity and you will not have lot’s of troubles about your back-end dependencies.

This would be also good for reproducing interesting bugs. Sometimes we had some very exceptional but also annoying issues that are very hard to reproduce with real data. With an opportunity like defining API responses in a simple way, it would be very simple to create some awkward situations for testing.

Another advantage would have a development environment that doesn’t really need to connect some remote servers, even internet. You would not need a working REST API server to work.

Then we created connect-api-mocker

What is connect-api-mocker?

connect-api-mocker is an npm package(actually a simple connect middleware) that can be used for creating fake responses for REST endpoints.

We created the first version in 2013 at one of my previous companies and also shared on Github. After I left there, I forked the repository and continued to improve it to use it on my clients’ projects. I also created an npm package and added many features.

connect-api-mocker, uses some mock json files in filesystem that are created with same folder structure with REST endpoints. This makes it very simple to use and modify it. But it’s not just that, there are more!

How is it easy to use mocking

You can use connect-api-mocker with Webpack, Grunt, Gulp and other connect.js powered environments. You can install connect-api-mocker with command:

npm i --save-dev connect-api-mocker

To use it with a webpack project, you need to use setup property of webpack-dev-server:

var apiMocker = require(‘connect-api-mocker’);
...
setup: function(app) {
    app.use('/api', apiMocker('mocks/api'));
},...

That configuration means, api mocker will try to respond requests that has url starts with /api by some files in the mocks/api folder. For example, if you make a GET request to /api/users/1 it will respond that request with file located inmocks/api/users/1/GET.json.

You already got the idea, right? The request mapping structure is like that:

[METHOD] /{baseUrl}/{path} > {basePath}/{path}/[METHOD].json

It will work all kind of request methods like POST, PUT, DELETE, OPTIONSetc. Some examples:

POST /api/users/1 ==> mocks/api/users/1/POST.json
DELETE /api/users/1 ==> mocks/api/users/1/DELETE.json

JSON files are normal, simple JSON files.

Custom responses

Creating mock json files with same path by request path is a good idea, but it’s not enough for all cases. If you also want to create some different mock responses for query or other request parameters, you will need more. This is where custom response support comes in.

Custom responses are simple connect middleware functions that also created in same path except(and of course) with a JS file extension. For example, instead of using simple POST.json file, you can create a POST.jsfile with content like that:

module.exports = function (request, response) {
    if (!request.get('X-Auth-Key')) {
        response.status(403).send({});
    } else {
        response.sendFile('POST.json', {root: __dirname});
    }
}

If you place that file to mocks/api/users/POST.js and if you make a POSTrequest to /api/users, api-mocker will run your custom response middleware instead of returning POST.json file in the path. In the custom response example above, we are testing existence of a header and if it’s not there we are responding with a 403 error, if it’s there, we are simply sending POST.json file content in the same folder.

Can you see the power of that functions?

Another example:

GET /api/users?type=active ==> mocks/api/users/GET.js

module.exports = function (request, response) {
    var targetFileName = 'GET.json';
    // Check is a type parameter exist
    if (request.query.type) {
        // Generate a new targetfilename with that type parameter
        targetFileName = 'GET_' + request.query.type + '.json';
        // If file does not exist then respond with 404 header
        if (!fs.accessSync(targetFileName)) {
            return response.status(404);
        }
    }
    // Respond with targetFileName
    response.sendFile(targetFileName, {root: __dirname});
}

With an example like above, we can send separate JSON responses depending on a query parameter.

And another example with another good opportunity:

GET /api/gateway/3213132 ==> mocks/api/gateway/3213132/GET.js

var requestCount = 0;

module.exports = function (request, response, next) {
    requestCount++;
    if (requestCount == 1) {
        response.json({
            status: 'notready'
        });
    } else {
        response.json({
            status: 'ready'
        });
    }
};

Because of connect servers are running as an instance, we can store some in-memory variables like the example above. In that example we are responding different result depending of request count to that endpoint.

As you can see, you don’t have any limit with custom responses!

Wildcard support

We need one more thing: defining some wildcards in path definitions. We need that because it’s not very cool to create folders for every ids of our models like users/1, users/2, users/3 etc. What is about to create o folder like users/__user_id__ and use that user_id variable in our custom responses as a request parameter? Yeah, that would be great! The good news is it’s already there!

So if you make a request something like that:

GET /api/projects/50b3c102–298a-45ad-acad-e21b6c1bbdcc/

You can define a mock in mocks/api/projects/50b3c102-298a-45ad-acad-e21b6c1bbdcc/GET.json If api mocker finds a file with that path, it will respond with that. But another option is creating a file in a path like mocks/api/projects/__project_uuid__/GET.json. In that case, all requests to projects/{something} that has not a specific mock file, will be responded with same response. Also it’s possible to define a custom response in the same folder with a GET.js file and produce a dynamic response depend on wildcard variable that defined in folder name( project_uuid ). For example:

module.exports = function (request, response) {
    response.json({
        id: request.params.project_uuid
    });
}

Wildcard definitions are starting and ending with double underscore. And it’s the last that api mocker checked. So looking for a mock response flow is like that:

  1. look for mocks/api/projects/50b3c102-298a-45ad-acad-e21b6c1bbdcc/GET.js
  2. look for mocks/api/projects/50b3c102-298a-45ad-acad-e21b6c1bbdcc/GET.json
  3. look for mocks/api/projects/__{something}__/GET.js
  4. look for mocks/api/projects/__{something}__/GET.json

Also you can define multiple nested wildcards for a path like: mocks/api/__resource_type__/__resource_uuid__/GET.json . That will be respond all GET requests to api/*/* .

NextOnNotFound setting

By default, if you enable api mocker for a path and you don’t define a mock file for a request, it will be respond with a 404 Not Found error. But it’s also available to set nextOnNotFound to true then all not found requests will be passed to next middleware. Sample configuration:

app.use('/api', apiMocker({
    target: 'mocks/api',
    nextOnNotFound: true
});

This is very powerful if you only want to mock some part of your endpoints instead of all of them. With the help of proxy features of Webpack, Grunt or Gulp, you can continue to get responses from your real api that you don’t want to mock. An example for webpack:

// webpack.config.js
...
setup: function (app) {
    app.use('/backend/api/v1/', apiMocker({
        target:         'mocks/api',
        nextOnNotFound: true  }));
    
    app.use('/analytics/api/v1/', apiMocker({
        target:         'mocks/analytics',
        nextOnNotFound: true  }));
},
proxy: {
    '/backend': {
        target: 'https://api.backend.yourdomain.com'
    },
    '/analytics': {
        target: 'https://api.analytics.yourdomain.com'
    }
}

In the example above, we defined 2 api mocker for 2 separate paths and also defined some proxies to real api servers for that paths. With the help of nextOnNotFound option, requests to that paths that doesn’t have a mock definition will be passed to that proxies.

Conclusion

I used connect-api-mocker nearly on all my projects for last 4–5 years with several teams and we were very happy for getting advantages of it. I also suggest to use it or some solutions like that in your projects to simplify and make fast your development environment. Because of it’s also open source, you are very welcome for your contributions.

https://github.com/muratcorlu/connect-api-mocker

Do you have an idea or a problem with api mocker? Just create an issue on Github repository and let’s talk about it!

Me on Mastodon: https://synaps.space/@murat