dream_http_client/recorder

Recorder process and state management

Manages HTTP request/response recordings using a process to store state. Supports recording, playback, and passthrough modes.

Types

Recorder operating mode

Determines how the recorder behaves when attached to HTTP requests.

Variants

  • Record(directory): Capture real HTTP requests/responses and save to disk
  • Playback(directory): Return recorded responses without making network calls
  • Passthrough: Make real requests without recording or playback

Examples

// Record real API calls
let record_mode = recorder.Record(directory: "mocks/api")

// Playback recorded responses
let playback_mode = recorder.Playback(directory: "mocks/api")

// No recording or playback
let passthrough_mode = recorder.Passthrough
pub type Mode {
  Record(directory: String)
  Playback(directory: String)
  Passthrough
}

Constructors

  • Record(directory: String)
  • Playback(directory: String)
  • Passthrough

Opaque recorder handle for managing HTTP request/response recordings

A Recorder is a handle to an OTP actor process that manages recording state. Multiple HTTP requests can share the same recorder by passing the same handle. The recorder handles saving recordings to disk, loading them for playback, and matching requests to recorded responses.

Thread Safety

Recorders are safe to use concurrently - multiple requests can use the same recorder handle simultaneously. The internal actor processes messages sequentially.

Lifecycle

  1. Create with recorder.start(mode, matching)
  2. Attach to requests with client.recorder(rec)
  3. Optionally cleanup with recorder.stop(rec) (recordings already saved)

Examples

// Record mode
let assert Ok(rec) = recorder.start(
  mode: recorder.Record(directory: "mocks"),
  matching: matching.match_url_only(),
)

// Use recorder with requests
client.new
  |> client.host("api.example.com")
  |> client.recorder(rec)
  |> client.send()

// Cleanup (optional - recordings already saved)
recorder.stop(rec)
pub opaque type Recorder

Values

pub fn add_recording(
  recorder: Recorder,
  rec: recording.Recording,
) -> Nil

Add a recording to the recorder

Manually adds a recording to the recorder’s in-memory state and saves it to disk immediately if in Record mode. This function is typically called automatically by the HTTP client when a request completes, but can be used directly for testing or manual recording creation.

Parameters

  • recorder: The recorder to add the recording to
  • rec: The recording (request/response pair) to add

Behavior by Mode

  • Record mode: Adds to in-memory state and saves to disk immediately
  • Playback mode: No-op (recordings loaded from disk at startup)
  • Passthrough mode: No-op (no recording functionality)

Examples

let assert Ok(rec) = recorder.start(
  mode: recorder.Record(directory: "mocks"),
  matching: matching.match_url_only(),
)

// Manually add a recording
let manual_recording = recording.Recording(
  request: create_test_request(),
  response: create_test_response(),
)
recorder.add_recording(rec, manual_recording)

Notes

  • Recordings are saved immediately in Record mode (no need to call stop())
  • If save fails, error is logged but recording remains in memory
  • Duplicate recordings (same signature) overwrite previous ones
pub fn find_recording(
  recorder: Recorder,
  request: recording.RecordedRequest,
) -> option.Option(recording.Recording)

Find a matching recording for a request

Searches for a recording that matches the given request based on the recorder’s matching configuration. This is used internally by the HTTP client during playback, but can be called directly to check if a recording exists.

Parameters

  • recorder: The recorder to search in
  • request: The request to find a matching recording for

Returns

  • Some(Recording): Matching recording found
  • None: No matching recording found (or not in Playback mode)

Examples

let assert Ok(rec) = recorder.start(
  mode: recorder.Playback(directory: "mocks"),
  matching: matching.match_url_only(),
)

let request = recording.RecordedRequest(
  method: http.Get,
  scheme: http.Https,
  host: "api.example.com",
  port: option.None,
  path: "/users",
  query: option.None,
  headers: [],
  body: "",
)

case recorder.find_recording(rec, request) {
  Some(recording) -> io.println("Found recording")
  None -> io.println("No recording found")
}

Notes

  • Only works in Playback mode (returns None in other modes)
  • Matching uses the recorder’s MatchingConfig (method, URL, headers, body)
  • Returns None if recorder doesn’t respond (safe default)
  • Timeout is 1 second - recorder should respond quickly
pub fn get_recordings(
  recorder: Recorder,
) -> List(recording.Recording)

Get all recordings from the recorder

Retrieves all recordings currently stored in the recorder’s in-memory state. In Playback mode, this returns all recordings loaded from disk at startup. In Record mode, this returns all recordings captured so far (including unsaved ones).

Parameters

  • recorder: The recorder to get recordings from

Returns

  • List(Recording): All recordings in the recorder (empty list if none or error)

Examples

let assert Ok(rec) = recorder.start(
  mode: recorder.Record(directory: "mocks"),
  matching: matching.match_url_only(),
)

// Make some requests...

let recordings = recorder.get_recordings(rec)
io.println("Captured " <> int.to_string(list.length(recordings)) <> " recordings")

Notes

  • Returns empty list if recorder doesn’t respond (safe default)
  • In Record mode, includes recordings that may not yet be saved to disk
  • Timeout is 1 second - recorder should respond quickly
pub fn is_record_mode(recorder: Recorder) -> Bool

Check if recorder is in Record mode

Determines whether the recorder is configured to capture and save real HTTP requests/responses. Useful for conditional logic that only applies during recording.

Parameters

  • recorder: The recorder to check

Returns

  • True: Recorder is in Record mode
  • False: Recorder is in Playback or Passthrough mode

Examples

let assert Ok(rec) = recorder.start(
  mode: recorder.Record(directory: "mocks"),
  matching: matching.match_url_only(),
)

if recorder.is_record_mode(rec) {
  io.println("Recording mode active")
}

Notes

  • Returns False if the recorder process doesn’t respond (safe default)
  • Timeout is 1 second - recorder should respond quickly
pub fn start(
  mode: Mode,
  matching_config: matching.MatchingConfig,
) -> Result(Recorder, String)

Start a new recorder in the specified mode

Creates an OTP actor process to manage recorder state. The recorder handles saving recordings to disk (Record mode), loading them for playback (Playback mode), or passing requests through unchanged (Passthrough mode).

Parameters

  • mode: The operating mode (Record, Playback, or Passthrough)
  • matching_config: Configuration for matching requests to recordings

Returns

  • Ok(Recorder): Successfully started recorder
  • Error(String): Error message if startup fails (e.g., cannot load recordings in Playback mode)

Examples

// Record mode - captures and saves requests/responses
let assert Ok(rec) = recorder.start(
  mode: recorder.Record(directory: "mocks/api"),
  matching: matching.match_url_only(),
)

// Playback mode - returns recorded responses
let assert Ok(playback_rec) = recorder.start(
  mode: recorder.Playback(directory: "mocks/api"),
  matching: matching.match_url_only(),
)

// Passthrough mode - no recording or playback
let assert Ok(passthrough_rec) = recorder.start(
  mode: recorder.Passthrough,
  matching: matching.match_url_only(),
)

Notes

  • In Playback mode, recordings are loaded from disk at startup
  • In Record mode, recordings are saved immediately when captured (no need to call stop())
  • Multiple requests can share the same recorder handle safely
  • The recorder process runs until stop() is called or the VM shuts down
pub fn stop(recorder: Recorder) -> Result(Nil, String)

Stop the recorder and cleanup

Stops the recorder’s OTP actor process and releases resources. In Record mode, recordings are already saved to disk immediately when captured, so this function only performs cleanup. Calling stop() is optional but recommended for proper resource management.

Parameters

  • recorder: The recorder to stop

Returns

  • Ok(Nil): Successfully stopped the recorder
  • Error(String): Error message if the recorder doesn’t respond within 5 seconds

Examples

let assert Ok(rec) = recorder.start(
  mode: recorder.Record(directory: "mocks"),
  matching: matching.match_url_only(),
)

// Use recorder...

// Cleanup (optional - recordings already saved)
case recorder.stop(rec) {
  Ok(_) -> io.println("Recorder stopped")
  Error(reason) -> io.println_error("Failed to stop: " <> reason)
}

Notes

  • Recordings are saved immediately when captured - stop() is not required for persistence
  • This function is optional but recommended for proper resource cleanup
  • Timeout is 5 seconds - recorder should stop quickly
  • After calling stop(), the recorder handle is no longer valid
Search Document