https://github.com/gr2m/node-http-recorder
Library agnostic in-process recording of http(s) requests and responses
https://github.com/gr2m/node-http-recorder
Last synced: about 1 year ago
JSON representation
Library agnostic in-process recording of http(s) requests and responses
- Host: GitHub
- URL: https://github.com/gr2m/node-http-recorder
- Owner: gr2m
- License: mit
- Created: 2021-12-21T01:01:28.000Z (over 4 years ago)
- Default Branch: main
- Last Pushed: 2025-04-09T22:30:38.000Z (about 1 year ago)
- Last Synced: 2025-04-09T23:28:21.880Z (about 1 year ago)
- Language: JavaScript
- Size: 263 KB
- Stars: 6
- Watchers: 2
- Forks: 2
- Open Issues: 2
-
Metadata Files:
- Readme: README.md
- Contributing: CONTRIBUTING.md
- License: LICENSE.md
- Code of conduct: CODE_OF_CONDUCT.md
Awesome Lists containing this project
README
# `@gr2m/http-recorder`
> Library agnostic in-process recording of http(s) requests and responses
[](https://github.com/gr2m/node-http-recorder/actions/workflows/test.yml)
## Features
- unopiniated, minimal library, as low-level as possible
- to record http(s) requests and responses within the same process, without starting a server or proxying
## Goals & trade-offs
I created `@gr2m/http-recorder` as a utility library that can be used as a building stone for more opiniated libraries. I'm one of the maintainers of [nock](https://github.com/nock/nock/) and hope to use this library to replace what `nock` is currently doing with much more code.
`@gr2m/http-recorder` plays well with [Mitm.js](https://github.com/moll/node-mitm) (or its [esm version](https://github.com/gr2m/mitm-esm)), which is another lowe-level library but focused on mocking http(s) and net requests.
Note that the requests & responses you receive from the `"record"` event is as-is. If the request or response was encoded then it's up to you to decode it if you need to.
## Install
```
npm install @gr2m/http-recorder
```
## Usage
```js
import http from "node:http";
import httpRecorder from "@gr2m/http-recorder";
httpRecorder.start();
httpRecorder.addListener(
"record",
({ request, response, requestBody, responseBody }) => {
const { method, protocol, host, path } = request;
const requestHeaders = request.getHeaders();
console.log(`> %s %s//%s%s`, method, protocol, host, path);
console.log(`> %j`, requestHeaders);
console.log(Buffer.concat(requestBody).toString());
const { statusCode, statusMessage, headers: responseHeaders } = response;
console.log(`\n< %s %s`, statusCode, statusMessage);
console.log(`< %j`, responseHeaders);
console.log(Buffer.concat(responseBody).toString());
},
);
const request = http.request("http://httpbin.org/post", { method: "post" });
request.write("data");
request.end();
// > POST http://httpbin.org/post
// > {"host":"httpbin.org"}
// data
//
// < 200 OK
// < {"content-type":"application/json",...}
// {
// "args": {},
// "data": "data",
// ...
// }
```
See more [examples](examples)
## API
`httpRecorder` is a singleton API.
### `httpRecorder.start()`
Hooks into the request life cycle and emits `record` events for each request sent through the `http` or `https` modules.
### `httpRecorder.stop()`
Removes the hooks. No `record` events will be emitted.
### `httpRecorder.addListener("record", listener)`
Subscribe to a `record` event. The `listener` callback is called with an options object
- `options.request`: an [`http.ClientRequest` instance](https://nodejs.org/api/http.html#class-httpclientrequest)
- `options.response`: an [`http.IncomingMessage` instance](https://nodejs.org/api/http.html#class-httpincomingmessage)
- `options.requestBody`: An array of Buffer chunks representing the request body
- `options.responseBody`: An array of Buffer chunks representing the response body
### `httpRecorder.removeListener("record", listener)`
Remove a `record` event listener.
### `httpRecorder.removeAllListeners()`
Removes all `record` event listeners.
## How it works
Once started, `httpRecorder` hooks itself into [the `http.ClientRequest.prototype.onSocket` method](https://github.com/nodejs/node/blob/cf6996458b82ec0bdf97209bce380e1483c349fb/lib/_http_client.js#L778-L782) which is conveniently called synchronously in [the `http.ClientRequest` constructor](https://nodejs.org/api/http.html#class-httpclientrequest).
When a request is intercepted, we
1. hook into [the `request.write` method](https://github.com/nodejs/node/blob/cf6996458b82ec0bdf97209bce380e1483c349fb/lib/_http_outgoing.js#L701-L711) and [the `request.end` method](https://github.com/nodejs/node/blob/cf6996458b82ec0bdf97209bce380e1483c349fb/lib/_http_outgoing.js#L833-L906) in order to clone the request body
2. subscribe to [the `response` event](https://nodejs.org/api/http.html#event-response)
3. hook into the `response.emit` method in order to clone the response body without consuming it
and then emit a `record` event with the `request`, `response`, `requestBody` and `responseBody` options.
## See also
- [`@gr2m/net-interceptor`](https://github.com/gr2m/node-net-interceptor) - Intercept outgoing network TCP/TLS connections
- [`@gr2m/http-interceptor`](https://github.com/gr2m/node-http-interceptor) - Intercept and mock outgoing http/https requests
## Contributing
See [CONTRIBUTING.md](CONTRIBUTING.md)
## Credit
The inspiration for hooking into `http.ClientRequest.prototype.onSocket` method comes from [Mitm.js](https://github.com/moll/node-mitm/#readme) - an http mocking library for TCP connections and http(s) requests.
## License
[MIT](LICENSE)