npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

fejk

v4.0.0

Published

A stateless mock with stateful capabilities

Downloads

252

Readme

Fejk

Build Status

Fejk is a simple stateless HTTP mocking service with a faux-stateful mocking mechanism based on cookies.

Fejk is intended to be consumed by a browser-like client, but should work fine for any HTTP client. Cookie capabilities are a prerequisite for faux statefulness.

ToC

Terminology

  • fejk - a running instance of fejk.
  • scenario - a collection of endpoints, stored in a .js file.
  • endpoints - an array of endpoint objects.
  • endpoint - an object containing a request and response field.
  • request - a subset of an Express.js request object.
  • response - status, data, headers and cookies with which to respond to a request.

Getting started

Prerequisites: Node.js ^12.5.0. Dependencies required in your project: body-parser, compression, cookie-parser, express.

const path = require('path');
const express = require('express');
const fejk = require('fejk');

const port = process.env.PORT || 9090;
const app = express();

// Feel free to mount the fejk express app under any route
app.use('/fejk', fejk({ path: path.join(__dirname, 'scenarios') }));

app.listen(port);

Configuration

Fejk accepts the following config options.

fejk({
  cors: {}, // Default: {}
  logger: customLogger, // Default: console
  path: '/path/to/scenarios', // Default: process.env.FEJK_PATH
  scenario: 'my-scenario', // Default: `default`
});
  • cors - options for the CORS Middleware, see configuration options for more info.
  • logger - a custom logger.
  • path - the path to the scenarios folder.
  • scenario - which scenario to use as default.

Scenario files

A scenario file is a .js file that should contain an object with an endpoints array. The file contents may be generated in any manner you wish, but be aware that it is included by require during execution.

Each endpoint must contain a request and response field. The request field is used to match against incoming requests to determine whether to use the response field to generate a response or not. If there are several matches the first match in the array of endpoints will be used.

request [object | function]

object

As an object, request needs to contain at least one key, but can contain any key that is present in an Express request.

Any key present in the request object must be present in the incoming Express request object and match exactly, with these exceptions:

  • path - The path in the request can be expressed as a regex.
  • objects - Objects in the request object only needs to be a subset of the corresponding field in the incoming Express request. This is useful for fields such as cookies.

function

As a function, the request field is passed the incoming express req as a first parameter, and is expected to return a boolean indicating whether the endpoint matches or not.

response [object]

The response object can contain the following properties:

status [number]

The status code to respond with. Optional, defaults to 200.

data [any | function]

Optional, defaults to 'OK'.

any

When data is anything other than a function, that field will be sent as the body of the response.

function

When data is a function, that function will be executed with the incoming Express request as its only parameter. The return value of the function will be sent as the body of the response.

Heads up! fejk does not use the require cache to store scenario files, meaning that the data function must be Pure. If you provide an impure function it will always respond with the initial state.

cookies [object]

Optional. Any cookies to set in the response. If omitted no cookies will be set.

headers [object]

Optional. Any headers to set in the response.

Selecting scenario

Fejk has one reserved optional query string parameter - scenario. This parameter specifies which scenario file to load. E.g. if you want to use the scenario from the file /scenarios/entries.js and you've configured the scenario path as /scenarios, the scenario parameter should be entries.

If the scenario parameter is not specified, fejk will attempt to load a default scenario. The default scenario must be stored in the root of the configured scenario path and be named default.js, or according to the configured option. See Configuration.

Example requests:

http://localhost:9090/fejk/items?scenario=items

In this request, the scenario parameter will determine which file to load, and the path (and potentially cookies) will determine which endpoint to load in that file.

http://localhost:9090/fejk/colors

In this request, the default scenario will be used.

Switching the default scenario

The default scenario used can be switched via an HTTP call to the /__scenario endpoint.

curl http://localhost:9090/__scenario -X POST -d '{"scenario":"new-scenario"}' -H 'Content-Type: application/json'

Examples and recipes

All of the examples below can be found in the provided example set of scenarios. See Running the examples

In this basic example the response will always be the same array.

module.exports = {
  endpoints: [
    {
      request: {
        method: 'GET',
        path: '/colors',
      },
      response: {
        data: ['red', 'green', 'blue'],
      },
    },
  ],
};
module.exports = {
  endpoints: [
    {
      request: {
        method: 'GET',
        path: '/items',
        cookies: {
          itemsposted: '1',
        },
      },
      response: {
        data: [
          {
            name: 'item one',
            id: '1',
          },
          {
            name: 'item two',
            id: '2',
          },
          {
            name: 'item three',
            id: '3',
          },
        ],
        cookies: {
          itemsposted: '',
        },
      },
    },
    {
      request: {
        method: 'GET',
        path: '/items',
      },
      response: {
        data: [
          {
            name: 'item one',
            id: '1',
          },
          {
            name: 'item two',
            id: '2',
          },
        ],
      },
    },
    {
      request: {
        method: 'POST',
        path: '/items',
        body: {
          name: 'item three',
        },
      },
      response: {
        status: '201',
        cookies: {
          itemsposted: '1',
        },
      },
    },
  ],
};

In this example an /items endpoint is provided. When performing a GET request to it the response will contain two items, since there should not be any cookie named itemsposted yet, and the first endpoint in the list will therefore not match.

After performing a POST request with the body {"name": "item three"} the itemsposted cookie will be set to '1'.

On the next GET request the first GET endpoint in the list will match since the incoming request now contains that cookie. From the browsers perspective the same endpoint is being queried, but the stored item is now present. On this GET request the cookie will also be set to an empty string as a means of cleanup.

Using the cookies, it is possible to create a service that appears to be stateful, but it is pushing the statefulness to the client, via cookies.

In this example a matcher function is used to check the path and query for certain values. Note that fejk can do this without defining a function, as illustrated in the second endpoint in the list.

module.exports = {
  endpoints: [
    {
      request(req) {
        return req.path === '/foo' && req.query.foo === 'bar';
      },
      response: {
        status: 200,
        data: () => 'matched by function, generated by function',
      },
    },
    // The function-based approach above can also be represented by the endpoint below.
    {
      request: {
        path: '/foo',
        query: { foo: 'bar' },
      },
      response: {
        status: 200,
        data: () => 'matched by object, generated by function',
      },
    },
    // The function-based approach is primarily intended for fuzzy, or complex matching.
    {
      request(req) {
        return req.headers.host.match(/foo\d\.com/);
      },
      response: {
        status: 200
      }
    }
  ],
};

This example illustrates how you could add an authentication checker to an entire scenario.

  module.exports = {
  endpoints: [
    // This endpoint is listed before the auth checker endpoint,
    // and will therefore not require authentication.
    {
      request: {
        path: '/authenticate',
        method: 'POST',
        // You would probably add restrictions on the payload here, this example skips that.
      },
      response: {
        status: 200,
        cookies: {
          token: 'auth-token',
        },
      },
    },
    // The auth checker will be 'pass through' if there is an auth cookie with the right content.
    // The negated condition can not be expressed by using the simpler request subset matching.
    {
      request(req) {
        // This could be any other property of the request, such as a header, instead.
        return req.cookies.token !== 'auth-token';
      },
      response: {
        status: 401,
        data: 'Authorization required',
      },
    },
    // If none of the previous endpoints matched,
    // the user is authenticated and is not trying to authenticate.
    {
      request: {
        path: '/secured-resource',
        method: 'GET',
      },
      response: {
        status: 200,
        data: 'Stuff only authorized users can see',
      },
    },
  ],
};

Running the examples

Clone the repository, yarn, yarn example and start sending requests to the fejk!

Hit the colors endpoint to see the defaults mechanism in action.

Here's some JS to paste into your console, check out the network tab while running these in the order GET POST GET.

GET

(function() {
var xhr = new XMLHttpRequest();
xhr.open("GET", "http://localhost:9090/fejk/items?scenario=items");

xhr.send();})();

POST

(function() {
var data = JSON.stringify({
  "name": "item three"
});

var xhr = new XMLHttpRequest();
xhr.open("POST", "http://localhost:9090/fejk/items?scenario=items");
xhr.setRequestHeader("content-type", "application/json");

xhr.send(data);})();