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

msw-auto-recorder

v0.1.0

Published

Automatically record and replay network requests for testing with MSW integration

Readme

AutoMock

A TypeScript library that integrates with MSW (Mock Service Worker) to automatically record and replay network requests for testing. It captures real API responses during development and serves them from cache in CI/CD environments.

Features

  • 🔄 Automatic Recording & Replay: Records real HTTP traffic during development, replays from cache in CI/CD
  • 🌐 HTTP Support: Full support for HTTP requests with automatic caching
  • 🔀 URL Replacement: Redirect requests to local test servers during recording
  • Fast Responses: 5ms delay for cached responses vs real network timing
  • 🎯 MSW Integration: Works seamlessly with existing MSW setup
  • 🔧 Environment Control: Toggle recording with AUTO_MOCK_RECORD environment variable
  • 📝 Helpful Error Messages: Clear guidance when requests fail or caches are missing
  • 🧩 Flexible API: Extensible API for custom handlers (WebSocket, GraphQL, etc.)

Installation

npm install msw-auto-recorder msw

Quick Start

import { setupServer } from 'msw/node'
import { AutoMock } from 'msw-auto-recorder'

// Create AutoMock with optional URL replacements
const autoMock = new AutoMock('./cache.json', {
  'api.production.com': 'localhost:3001'  // Redirect prod API to local test server
})

// Setup MSW server with HTTP handlers
const server = setupServer(
  ...autoMock.getFallbackHandlers()  // HTTP requests (uses flexible API internally)
)

server.listen({ onUnhandledRequest: 'bypass' })

// Set recording mode
process.env.AUTO_MOCK_RECORD = 'true'   // Enable recording
// process.env.AUTO_MOCK_RECORD = 'false' // Serve from cache only (CI/CD)

HTTP Requests (Automatic)

HTTP requests are automatically recorded and replayed using the flexible API internally:

// This will be recorded when AUTO_MOCK_RECORD=true
const response = await fetch('https://api.example.com/users')

// On subsequent runs with AUTO_MOCK_RECORD=false, serves from cache
const cachedResponse = await fetch('https://api.example.com/users')

Flexible API for Custom Handlers

AutoMock provides a powerful flexible API that allows you to create custom handlers for any protocol (WebSocket, GraphQL, custom protocols, etc.) while still benefiting from automatic cache recording and playback.

Note: The HTTP handler itself uses this flexible API, demonstrating best practices and DRY principles.

Basic Usage

// Example: Recording any data type
if (autoMock.isRecordingEnabled()) {
  // Record any data - strings, objects, arrays, etc.
  autoMock.record('API', 'https://api.example.com/users', 'query-params', {
    users: [{ id: 1, name: 'Alice' }],
    meta: { total: 1, page: 1 }
  })
  
  // Record simple data
  autoMock.record('CUSTOM', 'custom://identifier', 'input-data', 'simple response')
}

// Retrieve from cache
const userData = autoMock.getCached('API', 'https://api.example.com/users', 'query-params')
const simpleData = autoMock.getCached('CUSTOM', 'custom://identifier', 'input-data')

WebSocket Example

import { ws } from 'msw'
import { WebSocket } from 'ws'

// Example: Custom WebSocket handler using AutoMock's simplified API
const customWsHandler = ws.link('ws://localhost:4000/socket').addEventListener('connection', ({ client }) => {
  
  client.addEventListener('message', (event) => {
    const message = event.data.toString()
    
    // Check cache first
    const cached = autoMock.getCached('WS', 'ws://localhost:4000/socket', message)
    if (cached && !autoMock.isRecordingEnabled()) {
      // Serve from cache - cached data can be any format
      client.send(typeof cached === 'string' ? cached : JSON.stringify(cached))
      return
    }
    
    if (autoMock.isRecordingEnabled()) {
      // Recording mode: forward to real server and record response
      const realWs = new WebSocket('ws://real-server.com/socket')
      
      realWs.on('message', (response) => {
        // Record the raw response or any processed format
        autoMock.record('WS', 'ws://localhost:4000/socket', message, response.toString())
        
        // Forward to client
        client.send(response)
      })
      
      realWs.on('open', () => {
        realWs.send(message)
      })
    } else {
      // No cache and recording disabled
      client.send('{"error": "Not found in cache"}')
    }
  })
})

// Add to MSW server
const server = setupServer(
  ...autoMock.getFallbackHandlers(),
  customWsHandler
)

Advanced Example: GraphQL over WebSocket

// Example: GraphQL over WebSocket handler
const graphqlWsHandler = ws.link('ws://localhost:4000/graphql').addEventListener('connection', ({ client }) => {
  
  client.addEventListener('message', (event) => {
    const message = JSON.parse(event.data.toString())
    
    if (message.type === 'start') {
      const { query, variables } = message.payload
      const requestKey = JSON.stringify({ query, variables })
      
      // Check for cached GraphQL operation
      const cached = autoMock.getCached<{ requestData: any, responseData: string[] }>('GRAPHQL', 'ws://localhost:4000/graphql', requestKey)
      
      if (cached?.customData && !autoMock.isRecordingEnabled()) {
        // Serve cached GraphQL responses
        const responses = cached.customData.responseData
        responses.forEach((response, index) => {
          setTimeout(() => {
            client.send(JSON.stringify({
              id: message.id,
              type: 'data',
              payload: JSON.parse(response)
            }))
          }, index * 10)
        })
        
        setTimeout(() => {
          client.send(JSON.stringify({ id: message.id, type: 'complete' }))
        }, responses.length * 10)
        
      } else if (autoMock.isRecordingEnabled()) {
        // Record GraphQL operation
        const responses: string[] = []
        const realWs = new WebSocket('ws://real-graphql-server.com/graphql', 'graphql-ws')
        
        realWs.on('message', (data) => {
          const response = JSON.parse(data.toString())
          if (response.type === 'data') {
            responses.push(JSON.stringify(response.payload))
          } else if (response.type === 'complete') {
            // Record the complete GraphQL operation
            autoMock.record('GRAPHQL', 'ws://localhost:4000/graphql', requestKey, {
              status: 200,
              statusText: 'OK',
              headers: {},
              body: ''
            }, {
              requestData: { query, variables },
              responseData: responses
            })
          }
          client.send(data)
        })
        
        realWs.on('open', () => {
          realWs.send(JSON.stringify({ type: 'connection_init' }))
          realWs.send(event.data)
        })
      }
    }
  })
})

Environment Variables

  • AUTO_MOCK_RECORD=true: Enable recording of new requests
  • AUTO_MOCK_RECORD=false: Serve only from cache (recommended for CI/CD)

API Reference

Constructor

new AutoMock(cachePath?, urlReplacements?)
  • cachePath: Path to cache file (default: './auto-mock-cache.json')
  • urlReplacements: Object mapping URLs to redirect during recording

Core Methods

  • getFallbackHandlers() - Returns array of MSW HTTP handlers (using flexible API internally)
  • clearCache() - Clear all cached requests
  • getCacheStats() - Get cache statistics and debug info

Flexible API Methods

Recording

record<T>(method, url, requestData, responseData, customData?: T)

Records a response to cache. Used by HTTP handler and available for custom handlers.

  • method: Request method (e.g., 'GET', 'POST', 'WS', 'GRAPHQL')
  • url: Request URL
  • requestData: Request data to use as cache key
  • responseData: Response data with status, headers, body
  • customData: Optional custom data for non-HTTP protocols (typed)

Retrieval

getCached<T>(method, url, requestData?): (CacheResponse & { customData?: T }) | null

Get cached response including both HTTP response data and optional custom data.

Utilities

isRecordingEnabled(): boolean

Check if recording is currently enabled.

URL Replacement

Redirect requests during recording:

const autoMock = new AutoMock('./cache.json', {
  // Exact URL replacement
  'https://api.external.com/users': 'http://localhost:8080/users',
  
  // Domain-level replacement (preserves paths)
  'external-api.com': 'localhost:3001'
})

Error Handling

AutoMock provides helpful error messages:

  • HTTP: When recording disabled and cache empty, suggests enabling recording
  • HTTP: When remote server fails, provides URL replacement mapping suggestions
  • Custom Handlers: Can implement their own error handling using the flexible API

Design Philosophy

AutoMock follows DRY (Don't Repeat Yourself) principles:

  1. HTTP Handler as Example: The built-in HTTP handler uses the same flexible API available to users, serving as a reference implementation
  2. Unified Caching: All handlers (HTTP, custom) use the same cache format and methods
  3. Consistent Patterns: Recording and playback follow the same patterns across all handler types

Cache Statistics

Track different types of requests:

const stats = autoMock.getCacheStats()
console.log(`Total: ${stats.totalRequests}`)
console.log(`HTTP: ${stats.httpRequests}`)
console.log(`Custom: ${stats.customRequests}`)

Development

npm test     # Run tests
npm run build # Build the project

Migration from GraphQL WebSocket Support

Previous versions included built-in GraphQL over WebSocket support. The new flexible API allows you to implement GraphQL WebSocket handlers with more control and customization. See examples above for implementation patterns.

License

MIT