Skip to content

Commit

Permalink
feat(http): onFetchRequest (#2048)
Browse files Browse the repository at this point in the history
* feat(http): add onFetchRequest

* chore: changeset

* test: add

* chore: snaps

* Update http.test.ts

---------

Co-authored-by: jxom <jakemoxey@gmail.com>
  • Loading branch information
tmm and jxom authored Mar 31, 2024
1 parent 6de8712 commit 85c3695
Show file tree
Hide file tree
Showing 6 changed files with 91 additions and 17 deletions.
5 changes: 5 additions & 0 deletions .changeset/chatty-berries-brake.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"viem": patch
---

Added `onFetchRequest` to `http` transport.
16 changes: 16 additions & 0 deletions site/pages/docs/clients/transports/http.md
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,22 @@ const transport = http('https://eth-mainnet.g.alchemy.com/v2/...', {
})
```

### onFetchRequest (optional)

- **Type:** `(request: Request) => void`

A callback to handle the fetch request. Useful for logging or debugging.

```ts twoslash
import { http } from 'viem'
// ---cut---
const transport = http('https://eth-mainnet.g.alchemy.com/v2/...', {
onFetchRequest(request) {
console.log(request) // [!code focus]
}
})
```

### onFetchResponse (optional)

- **Type:** `(response: Response) => void`
Expand Down
20 changes: 20 additions & 0 deletions src/clients/transports/http.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,26 @@ describe('request', () => {
await server.close()
})

test('behavior: onFetchRequest', async () => {
const server = await createHttpServer((_, res) => {
res.end(JSON.stringify({ result: '0x1' }))
})

const requests: Request[] = []
const transport = http(server.url, {
key: 'mock',
onFetchRequest(request) {
requests.push(request)
},
})({ chain: localhost })

await transport.request({ method: 'eth_blockNumber' })

expect(requests.length).toBe(1)

await server.close()
})

test('behavior: onFetchResponse', async () => {
const server = await createHttpServer((_, res) => {
res.end(JSON.stringify({ result: '0x1' }))
Expand Down
8 changes: 5 additions & 3 deletions src/clients/transports/http.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,9 @@ export type HttpTransportConfig = {
* @link https://developer.mozilla.org/en-US/docs/Web/API/fetch
*/
fetchOptions?: HttpRpcClientOptions['fetchOptions'] | undefined
/**
* A callback to handle the response from `fetch`.
*/
/** A callback to handle the response from `fetch`. */
onFetchRequest?: HttpRpcClientOptions['onRequest'] | undefined
/** A callback to handle the response from `fetch`. */
onFetchResponse?: HttpRpcClientOptions['onResponse'] | undefined
/** The key of the HTTP transport. */
key?: TransportConfig['key'] | undefined
Expand Down Expand Up @@ -78,6 +78,7 @@ export function http(
fetchOptions,
key = 'http',
name = 'HTTP JSON-RPC',
onFetchRequest,
onFetchResponse,
retryDelay,
} = config
Expand All @@ -91,6 +92,7 @@ export function http(

const rpcClient = getHttpRpcClient(url_, {
fetchOptions,
onRequest: onFetchRequest,
onResponse: onFetchResponse,
timeout,
})
Expand Down
30 changes: 28 additions & 2 deletions src/utils/rpc/http.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,32 @@ describe('request', () => {
await server.close()
})

test('onRequest', async () => {
const server = await createHttpServer((_, res) => {
res.end(JSON.stringify({ result: '0x1' }))
})

const requests: Request[] = []
const client = getHttpRpcClient(server.url, {
onRequest: (request) => {
requests.push(request)
},
})
await client.request({
body: { method: 'web3_clientVersion' },
})
await client.request({
body: { method: 'web3_clientVersion' },
onRequest: (request) => {
requests.push(request)
},
})

expect(requests.length).toBe(2)

await server.close()
})

test('onResponse', async () => {
const server = await createHttpServer((_, res) => {
res.end(JSON.stringify({ result: '0x1' }))
Expand Down Expand Up @@ -303,12 +329,12 @@ describe('http (batch)', () => {
).toMatchInlineSnapshot(`
[
{
"id": 70,
"id": 74,
"jsonrpc": "2.0",
"result": "anvil/v0.2.0",
},
{
"id": 71,
"id": 75,
"jsonrpc": "2.0",
"result": "anvil/v0.2.0",
},
Expand Down
29 changes: 17 additions & 12 deletions src/utils/rpc/http.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,24 +14,28 @@ import { stringify } from '../stringify.js'
import { idCache } from './id.js'

export type HttpRpcClientOptions = {
// Request configuration to pass to `fetch`.
/** Request configuration to pass to `fetch`. */
fetchOptions?: Omit<RequestInit, 'body'> | undefined
// A callback to handle the response.
/** A callback to handle the request. */
onRequest?: ((request: Request) => Promise<void> | void) | undefined
/** A callback to handle the response. */
onResponse?: ((response: Response) => Promise<void> | void) | undefined
// The timeout (in ms) for the request.
/** The timeout (in ms) for the request. */
timeout?: number | undefined
}

export type HttpRequestParameters<
TBody extends RpcRequest | RpcRequest[] = RpcRequest,
> = {
// The RPC request body.
/** The RPC request body. */
body: TBody
// Request configuration to pass to `fetch`.
/** Request configuration to pass to `fetch`. */
fetchOptions?: HttpRpcClientOptions['fetchOptions'] | undefined
// A callback to handle the response.
/** A callback to handle the response. */
onRequest?: ((request: Request) => Promise<void> | void) | undefined
/** A callback to handle the response. */
onResponse?: ((response: Response) => Promise<void> | void) | undefined
// The timeout (in ms) for the request.
/** The timeout (in ms) for the request. */
timeout?: HttpRpcClientOptions['timeout'] | undefined
}

Expand Down Expand Up @@ -60,6 +64,7 @@ export function getHttpRpcClient(
const {
body,
fetchOptions = {},
onRequest = options.onRequest,
onResponse = options.onResponse,
timeout = options.timeout ?? 10_000,
} = params
Expand All @@ -72,7 +77,7 @@ export function getHttpRpcClient(
try {
const response = await withTimeout(
async ({ signal }) => {
const response = await fetch(url, {
const request = new Request(url, {
...fetchOptions,
body: Array.isArray(body)
? stringify(
Expand All @@ -94,6 +99,8 @@ export function getHttpRpcClient(
method: method || 'POST',
signal: signal_ || (timeout > 0 ? signal : null),
})
if (onRequest) await onRequest(request)
const response = await fetch(request)
return response
},
{
Expand All @@ -108,11 +115,9 @@ export function getHttpRpcClient(
let data: any
if (
response.headers.get('Content-Type')?.startsWith('application/json')
) {
)
data = await response.json()
} else {
data = await response.text()
}
else data = await response.text()

if (!response.ok) {
throw new HttpRequestError({
Expand Down

0 comments on commit 85c3695

Please sign in to comment.