Mock Server
Introduction

Creating mock APIs is a critical aspect of software development, whether for testing purposes or rapid prototyping. APIGIT MockServer offers a streamlined solution by wrapping around Express.js, providing a simplified environment to define mock server behavior without the need to manually set up and configure an Express application.

APIGIT MockServer operates within a Node.js sandbox, leveraging an Express.js app to simplify the creation of mock APIs. It enables rapid development and handling of various HTTP requests through straightforward definitions. In this introduction, we’ll explore MockServer’s features, highlight its capabilities, and demonstrate how you can quickly get started with practical examples.

Key Features of MockServer

  • Express.js Wrapper: MockServer wraps around Express.js, making it familiar and easy to use for developers who already know Express.js.
  • Pre-installed Libraries: Popular JavaScript libraries such as lodash, moment, faker, and more are pre-installed, streamlining the creation of dynamic mock responses.
  • Supports Routes and Middleware: Define routes and middleware just like you would in an Express application.
  • Stateful Data: Easily maintain data between requests using a global state variable, making it perfect for simulating real-world behavior.
  • Modular Organization: Mock server scripts are modularly organized, similar to daily development practices, making it easy to manage and reuse code.

How MockServer Works

MockServer works by allowing you to define API routes using mock object. mock is a global Express.js app automatically created by the underlying apigit mock server framework. You can define routes using mock.[method], following the guidelines provided in the Express.js documentation.

Here's an overview of how MockServer simplifies creating APIs:

  • Use mock.[method](...) to define routes and request handlers.
  • Use standard Express.js methods like req and res to handle HTTP requests and responses.
  • Pre-installed libraries eliminate the need for manual installation, allowing you to focus on defining your mock logic.

Example: Defining Routes and Middleware

The following example shows how easy it is to create routes using MockServer:

mock.get("/user/:name", function (req, res) {
	return res.status(200).send("hello" + req.params.name);
});
 
mock.post('/test-urlencoded', (req, res) => {
	console.log('Received x-www-form-urlencoded data:', req.body);
	res.status(200).send({ message: 'Received x-www-form-urlencoded data', data: req.body });
});
 
mock.get('/testauth', exampelAuthMiddleware, (req, res) => {
	res.status(200).send('This route is to deomostrate auth through middleware ');
});
 
mock.get('/testmd2', firstMiddleware,  secondMiddleware, (req, res) => {
	res.status(200).send('This route has multiple middlewares!');
});
 
// !!!== mock.define is deprecated and provided for compatibility only. Please use mock.[method] instead, as it follows the native Express.js approach. !!!
mock.define("/user/:name", "DELETE", function(req, res) {
  // handle req
  res.status(200).send("success");
});

Please note that mock.define() has been deprecated and is provided only for compatibility purposes. We recommend using mock.[method] instead, as it aligns with the native Express.js approach.

Just like in Express.js, you can use middleware to process requests before they reach the final route handler:

const exampelAuthMiddleware = (req, res, next) => {
  const authHeader = req.headers['authorization'];
  if (!authHeader) {
    return res.status(401).send('Authorization header missing');
  }
 
  const token = authHeader.split(' ')[1];
  if (!token) {
    return res.status(401).send('Token missing');
  }
 
  try {
    const SECRET_KEY = 'your-secret-key';
    const decoded = jwt.verify(token, SECRET_KEY);
    req.user = decoded;
    next();
  } catch (err) {
    return res.status(403).send('Invalid token');
  }
};
 
function firstMiddleware(req, res, next) {
	console.log('First middleware');
	next();
}
 
function secondMiddleware(req, res, next) {
	console.log('Second middleware');
	next();
}
 
mock.use((req, res, next) => {
	console.log("middware for all routes");
	next();
});
 
mock.get('/testauth', exampelAuthMiddleware, (req, res) => {
	res.status(200).send('This route is to deomostrate auth through middleware ');
});
 
mock.get('/testmd2', firstMiddleware,  secondMiddleware, (req, res) => {
	res.status(200).send('This route has multiple middlewares!');
});
   

The mock.use() function can be used to define middleware for all routes or specific paths, just like in an Express app.

Working with Requests and Responses

MockServer provides a simple way to interact with requests and responses using standard Express.js conventions. Below are some examples of common tasks:

Handling Route Parameters and Query Parameters

mock.get('/repository/:user/:repo', (req, res) => {
  const { user, repo } = req.params;
  res.json({
    message: 'Repository Details',
    user: user,
    repo: repo
  });
});
 
mock.get('/search', (req, res) => {
  const { keyword, page, limit } = req.query;
  res.json({
    message: 'Search Parameters Received',
    keyword: keyword || 'none',
    page: page ? parseInt(page, 10) : 1,
    limit: limit ? parseInt(limit, 10) : 10
  });
});

Setting Response Status and Headers

mock.get('/not-found', (req, res) => {
  res.status(404).json({
    message: 'Resource not found',
    code: 404
  });
});
 
mock.get('/single-header', (req, res) => {
  res.set('Content-Type', 'application/json');
  res.set('X-Custom-Header', ['value1', 'value2']);
  res.json({ message: 'Headers set successfully!' });
});

Using Pre-Installed Libraries

MockServer comes with popular JavaScript libraries pre-installed, so you can use them directly in your mock definitions:

const _ = require('lodash');
const moment = require('moment');
const { faker } = require('@faker-js/faker');
 
mock.define('/test-libs', 'GET', (req, res) => {
  const randomUser = {
    id: faker.datatype.uuid(),
    name: faker.name.findName(),
    email: faker.internet.email()
  };
  res.json({
    currentDate: moment().format(),
    activeUsers: _.filter([{ active: true }, { active: false }], { active: true }),
    randomUser: randomUser
  });
});

These libraries help you create rich, dynamic responses easily without the need for additional installations. If you need a library that is not listed, please reach out to APIGIT, and we will evaluate and add it quickly. The supported libraries include:

  • @faker-js/faker (v9.2.0) - data generator
  • ajv (v8.17.1) - JSON schema validation
  • base64-js (v1.5.1) - Base64 encoding/decoding
  • cors (v2.8.5) - Cross-Origin Resource Sharing middleware
  • jsonwebtoken (v9.0.2) - JWT creation and verification
  • lodash (v4.17.21) - general utility
  • moment (v2.30.1) - time and date handling
  • uuid (v11.0.3) - UUID generator
  • validator (v13.12.0) - validation helper

Stateful Data with MockServer

MockServer provides a state object, which allows you to store and retrieve data between requests, making it easy to simulate persistent state:

// Create a stateful list of users
state.users = state.users || [];
 
mock.post('/users', (req, res) => {
  if (!req.body.username) {
    return res.status(400).json({ status: "error", details: "Missing username" });
  }
 
  state.users.push(req.body);
  return res.status(200).json({ status: "ok" });
});

Modular Mock Scripts with MockServer

To maintain cleaner and more organized code, MockServer supports splitting mock scripts into multiple files in a modular way, similar to everyday development practices. Typically, you would have a main.js file that defines all the routes, which then requires handler definitions from separate files. This allows you to create reusable, well-structured modules for route handlers, keeping your codebase maintainable.

For example:

  • main.js: The main file that defines all routes and requires other files for the handler definitions. The handler modules are required at the beginning of the file for better readability and organization. For example, handlers like adding or deleting users can be defined in one file and exported together using module.exports.
// main.js
 
// Require handler modules at the top
const { userPostHandler, userDeleteHandler } = require('./handlers/userHandlers');
 
// Define routes
mock.post('/user/:name', userPostHandler);
mock.delete('/user/:name', userDeleteHandler);
  • handlers/userHandlers.js: A separate file that exports functions to handle different user-related requests, using module.exports to export them together.
// handlers/userHandlers.js
 
function userPostHandler(req, res) {
  res.status(404).send('User not found');
}
 
function userDeleteHandler(req, res) {
  res.status(200).send('User deleted successfully');
}
 
module.exports = {
  userPostHandler,
  userDeleteHandler
};

This modular approach helps you organize routes logically and enables reusability across different mock projects.

Conclusion

MockServer simplifies the process of setting up mock APIs by leveraging the power of Express.js in a sandboxed environment. With built-in libraries, easy route definitions, middleware support, and stateful data management, it’s a convenient solution for developers looking to create mock APIs quickly and efficiently.

Whether you’re a beginner or an experienced developer, MockServer provides the tools you need to simulate API behavior without the overhead of setting up a full Express server. Give it a try, and make your API testing and prototyping smoother and more productive.