Skip to content

Getting Started

Record, Redact, and Replay HTTP traffic in 5 minutes.

Prerequisites

  • Go 1.22 or later (for the Go library)
  • Or: Docker (for CLI/Docker usage with any language)
  • go get github.com/VibeWarden/httptape

Step 1: Record HTTP traffic

Wrap any http.RoundTripper with a Recorder to capture traffic:

package main

import (
    "fmt"
    "net/http"

    "github.com/VibeWarden/httptape"
)

func main() {
    // Create a file-backed store for persistent fixtures
    store, err := httptape.NewFileStore(httptape.WithDirectory("./fixtures"))
    if err != nil {
        panic(err)
    }

    // Create a recorder that wraps http.DefaultTransport
    rec := httptape.NewRecorder(store,
        httptape.WithRoute("github-api"),
    )
    defer rec.Close() // Always close to flush pending recordings

    // Use it as a normal http.Client
    client := &http.Client{Transport: rec}
    resp, err := client.Get("https://api.github.com/users/octocat")
    if err != nil {
        panic(err)
    }
    defer resp.Body.Close()

    fmt.Println("Recorded:", resp.StatusCode)
    // A JSON fixture file is now saved in ./fixtures/
}

After running this, check ./fixtures/ -- you will see a JSON file containing the full request and response.

Step 2: Replay recorded traffic

Serve the recorded fixtures as a mock HTTP server:

package main

import (
    "fmt"
    "net/http"
    "net/http/httptest"

    "github.com/VibeWarden/httptape"
)

func main() {
    store, err := httptape.NewFileStore(httptape.WithDirectory("./fixtures"))
    if err != nil {
        panic(err)
    }

    srv := httptape.NewServer(store)
    ts := httptest.NewServer(srv)
    defer ts.Close()

    // Requests are matched against recorded tapes by method + path
    resp, err := http.Get(ts.URL + "/users/octocat")
    if err != nil {
        panic(err)
    }
    defer resp.Body.Close()

    fmt.Println("Replayed:", resp.StatusCode) // 200
}

Step 3: Redact sensitive data

Strip secrets and PII before anything touches disk:

package main

import (
    "net/http"

    "github.com/VibeWarden/httptape"
)

func main() {
    store, err := httptape.NewFileStore(httptape.WithDirectory("./fixtures"))
    if err != nil {
        panic(err)
    }

    // Build a sanitization pipeline
    sanitizer := httptape.NewPipeline(
        httptape.RedactHeaders(),                              // redact Authorization, Cookie, etc.
        httptape.RedactBodyPaths("$.password", "$.ssn"),       // redact specific body fields
        httptape.FakeFields("my-project-seed", "$.user.email", "$.user.id"), // deterministic fakes
    )

    rec := httptape.NewRecorder(store,
        httptape.WithRoute("users-api"),
        httptape.WithSanitizer(sanitizer),
    )
    defer rec.Close()

    client := &http.Client{Transport: rec}
    client.Post("https://api.example.com/users", "application/json",
        nil, // your request body here
    )

    // The fixture file now has:
    // - Authorization header replaced with "[REDACTED]"
    // - $.password replaced with "[REDACTED]"
    // - $.user.email replaced with "user_a1b2c3d4@example.com" (deterministic)
    // - $.user.id replaced with a deterministic number
}

Step 4: Use in tests

The most common pattern -- record once, replay in every test run:

package myapi_test

import (
    "net/http"
    "net/http/httptest"
    "testing"

    "github.com/VibeWarden/httptape"
)

func TestUserAPI(t *testing.T) {
    store, err := httptape.NewFileStore(
        httptape.WithDirectory("testdata/fixtures"),
    )
    if err != nil {
        t.Fatal(err)
    }

    srv := httptape.NewServer(store)
    ts := httptest.NewServer(srv)
    defer ts.Close()

    // Point your API client at the mock server
    resp, err := http.Get(ts.URL + "/users/octocat")
    if err != nil {
        t.Fatal(err)
    }

    if resp.StatusCode != 200 {
        t.Errorf("expected 200, got %d", resp.StatusCode)
    }
}

Using in-memory store for tests

For tests that don't need persistent fixtures:

func TestWithMemoryStore(t *testing.T) {
    store := httptape.NewMemoryStore()

    // Record
    rec := httptape.NewRecorder(store, httptape.WithRoute("test"))
    client := &http.Client{Transport: rec}
    client.Get("https://api.example.com/data")
    rec.Close()

    // Replay
    srv := httptape.NewServer(store)
    ts := httptest.NewServer(srv)
    defer ts.Close()

    resp, _ := http.Get(ts.URL + "/data")
    // assert on resp...
    _ = resp
}

Next steps

  • Recording -- async/sync modes, sampling, body size limits
  • Redaction -- full guide to redaction and faking
  • Proxy Mode -- forward to upstream with fallback-to-cache
  • Matching -- control how requests match recorded tapes
  • CLI -- use httptape as a standalone tool (any language)