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 🙏

© 2026 – Pkg Stats / Ryan Hefner

@sr1ch1/apollo-fetcher-stub

v1.6.1

Published

A stub version of the apollo fetcher to be used for integration testing.

Readme

GitHub Workflow Status Coverage Status npm GitHub

apollo-fetcher-stub

A stub version of the Apollo Server's fetcher interface to simplify integration testing of resolvers that are based on Apollo Server's RESTDataSource.

Introduction

This package provides a stub implementation of the fetcher interface that is useful when creating integration tests for resolvers in the Apollo Server 4. Requests along with corresponding responses can be specified through the fluent interface of this library.

Overview

Apollo Server 4 uses a multi-step request pipeline to execute incoming GraphQL requests. It allows using plugins for each step. The plugins may affect the outcome of a GraphQL operation. This makes it important to test the server with plugins enabled.

To enforce separation of concerns and simplify writing integration tests the use of following pattern is recommended when fetching data from REST-based services:

apollo-fetcher-stub.svg

A recommended pattern for implementing resolvers is to use data sources to retrieve/modify data. As it is a common use case to fetch data from REST-based services Apollo Server comes with a RESTDataSource class that can be used as a base class for specific data source implementation. The RESTDataSource makes use of a specific fetcher interface in order to perform HTTP requests. The fetcher interface can be used to plug in different fetch libraries (node-fetch, make-fetch-happen, or undici).

This library uses the same interface to inject a stub fetcher that can be instrumented for integration testing.

Installation

Requirements

To install the package use:

npm install --save-dev sr1ch1/apollo-fetcher-stub

or with yarn:

yarn add -D sr1ch1/apollo-fetcher-stub

Usage

As this library is intended to be used as a fetcher replacement the documentation will focus on the described pattern above. It is not are requirement to rely on the RESTDataSource class. The fetcher stub can be used in any context where Apollo's fetcher interface is used.

Premise

To give a better context in the use of the fetcher stub, it is assumed that you have implemented a data source based on the RESTDataSource

// this is the implementation of a specific data source based on Apollo Server's RESTDataSource
export class ExampleAPI extends RestDataSource {
  // ... implementation of getData as an example of a function to be made available
  // if this contains nontrivial transformations, you may also want to test this.
}

Furthermore, you have a resolver that uses this data source

export const exampleResolver = {
  Query: {
    populations: async (_, __, {dataSources}): Promise<void>  => {
      // fetch data via data source. Here we are using the exampleAPI, 
      // that is injected as a data source.
      const result = await dataSources.exampleAPI.getData()
      // ... the logic you want to test along with the pipeline logic you might have in place.
    }
  },
};

Setting up an integration Test

To create an integration test, we need the following steps:

  1. Create and set up the fetcher stub. It must respond to the expected requests with appropriate data. This step is specific for each integration test. In this example the fetcher stub will return the status code 204 whenever it receives a get request with the URL http://localhost:8080/ping
// import the stub
import {FetcherStub} from "@sr1ch1/apollo-fetcher-stub";

// first create a new stub instance
const fetcherStub = new FetcherStub()

// when the fetcher receives a specific get request
fetcherStub.get("http://localhost:8080/ping")
// it responds with this status code
.responds().withStatusCode(204);
  1. Create an Apollo context function that makes use of the fetcher stub.
const createContext = async (): Promise<ContextValue> => {
  return {
    dataSources: {
      // here we create the data source and injecting it with the fetcher stub
      example: new ExampleAPI({ fetcher: fetcherStub }),
    }
  };
}
  1. Create an Apollo test server that is using a configuration close to your production server.
// For the integration tests, create a test server that is using
// the prepared context for the text
const testServer = new ApolloServer<ContextValue>({
  typeDefs, // the real schema 
  resolvers,// the real resolvers
  plugins   // the real plugins
});
  1. Run the desired GraphQL query on the test server using the prepared context.
// create the context with the stub fetcher and real rest data sources.
const contextValue = await createContext(); 

// execute the test GraphQL query using the test server with the real configuration
// the only thing that is stubbed is the fetcher the RESTDataSource is using.
// This ensures we test as much of the Apollo Stack as possible.
const response = await testServer.executeOperation({ query }, { contextValue });
  1. the response is a GraphQLResponse object and can be tested by querying the available properties.
// in this example it is assumed we receive a single data set and
// directly test the properties (example with Jest)
expect(response?.body?.kind).toBe('single');
expect(response.body['singleResult']).toBeDefined();
expect(response.body['singleResult']['data']).toBeDefined();
const myData = response.body['singleResult']['data']['myData'];
// test the properties of myData ...

Fluent interface of the fetcher stub

The fetcher stub has a fluent interface that allows you to create a stub in a declarative and readable way. This helps keeping the integration tests clean. The interface is modelled after the structure of an HTTP request. It does not provide any kind of validation though, to be open for writing tests with invalid requests. The following Http methods are available:

const fetcherStub = new FetcherStub()
const URL = 'http://localhost:4000/some-path'

fetcherStub.get(URL);
fetcherStub.head(URL);
fetcherStub.post(URL);
fetcherStub.put(URL);
fetcherStub.patch(URL);
fetcherStub.delete(URL);
fetcherStub.options(URL);

After specifying the HTTP method, you can add any header you need:

fetcherStub.get(URL)
  .withHeader('Accept', 'application/json')
  .withHeader('User-Agent', 'Mozilla/5.0 (platform; rv:geckoversion) Gecko/geckotrail Firefox/firefoxversion');

For POST or PATCH requests, you can also add a body like this:

fetcherStub.get(URL)
  .withHeader('User-Agent', 'Mozilla/5.0 (platform; rv:geckoversion) Gecko/geckotrail Firefox/firefoxversion');
  .withBody('{name:"Max"}', 'application/json')

The second parameter, the content type is optional. You could also use the withHeader function to specify a content type.

After specifying the request, we need to configure what the response would be. This can be as simple as returning a status code:

fetcherStub.get(URL)
  .responds()
  .withStatusCode(200);

// or alternatively
fetcherStub.get(URL)
  .responds()
  .withStatusCode(201)
  .withStatusText('Ok');

// or
fetcherStub.get(URL)
  .responds()
  .withStatus(201, 'Ok');

But if response headers and body are needed this can be done like this:

fetcherStub.get(URL)
  .responds()
  .withStatusCode(200)
  .withHeader('Server', 'Some Server')
  .withBody('{"name":"Max", "age":33}', 'application/json')

The second parameter, the content type is also optional and if desired the content type can be specified directly as a header.

With these functions even more complex scenarios can be handled. The fetcher stub collects the specification and uses it during the integration test. You could specify multiple different requests should that be necessary. Complex scenarios can be covered this way.

Here is an example how to stub a preflight request (CORS scenario):

fetcherStub.options("/")
  .withHeader('Host', 'service.example.com')
  .withHeader('Origin', 'https://www.example.com')
  .withHeader('Access-Control-Request-Method', 'PUT')
  .responds()
  .withHeader('Access-Control-Allow-Origin', 'https://www.example.com')
  .withHeader('Access-Control-Allow-Methods', 'PUT')
  .withStatusCode(200)
  .withStatusText('Ok');

By adding multiple requests, it is also possible to handle OAUTH scenarios. This is useful when you want to test login scenarios in a fast and reliable way.

Sometimes it is not good enough to provide a static URL that must be stubbed. This can be the case when you cannot completely control the URL to test with. For example, when the resolver uses random values, time dependent values or other values that you cannot influence. In this special case you can pass a regular expression instead of a string as URL. As an example we could have a scenario where the uncontrollable piece of data is a query parameter named code. We only know the structure of the code not the exact value. We can create a regular expression for it and use it as parameter. Here it is assumed that the code consists of capital letters and digits and is exactly 8 characters long.

const fetcherStub = new FetcherStub()
const URL = /http:\/\/localhost:4000\/some-path?code=[A-Z0-9]{8}/

fetcherStub.get(URL);